3

Je veux remplacer ces macros avec un modèle variadique qui réalise la même chose.Comment créer une fonction de modèle variadique pour déplacer les valeurs d'argument et gérer à la fois les valeurs lvalues ​​et les valeurs rvalues?

#define SHFT2(a, b, c) do { (a) = (b); (b) = (c); } while(0) 
#define SHFT3(a, b, c, d) do { (a) = (b); (b) = (c); (c) = (d); } while(0) 
#define SHFT4(a, b, c, d, e) do { (a) = (b); (b) = (c); (c) = (d); (d) = (e); } while(0) 

J'ai une solution qui fonctionne pour lvalues ​​

template<typename T, typename... Ts> 
T first(T t, Ts... ts) 
{ 
    return t; 
} 

template<typename T> 
void shift(T t) 
{ 
    // do nothing 
} 

template<typename T, typename... Ts> 
void shift(T& t, Ts&... ts) 
{ 
    t = first(ts...); 
    shift(ts...); 
} 

Par exemple, cela fonctionne

int w = 1; 
int x = 2; 
int y = 3; 
int z = 4; 

shift(w, x, y, z); 

printf("%d %d %d %d\n", w, x, y, z); // 2 3 4 4 

Mais je veux être en mesure de passer dans un rvalue à la fin

shift(w, x, y, z, 5); 

printf("%d %d %d %d\n", w, x, y, z); // expect 2 3 4 5 

J'ai l'erreur

test.cpp:31:2: error: no matching function for call to 'shift' 
     shift(w, x, y, z, 5); 
     ^~~~~ 
test.cpp:16:6: note: candidate function [with T = int, Ts = <int, int, int, int>] not viable: expects an l-value for 5th 
     argument 
void shift(T& t, Ts&... ts) 
    ^
test.cpp:10:6: note: candidate function template not viable: requires single argument 't', but 5 arguments were provided 
void shift(T t) 

car vous ne pouvez pas référencer un rvalue.

Comment puis-je faire cela fonctionner dans les deux cas?

+0

Qu'en est-il de la macro que vous avez mentionnée? Je suppose que cela produira le même genre d'erreur. Affecter quelque chose à un littéral comme 5 de toute façon n'a pas de sens. Ou, avez-vous l'intention de mettre une valeur seulement à la dernière position? –

+0

@JunekeyJeon Correct. Il n'y aura jamais qu'une valeur à la dernière position. – strobot

Répondre

1

Vous pouvez utiliser un paramètre de référence de transfert afin d'accepter les lvalues ​​et rvalues ​​et std::forward à « avant » la catégorie de valeur de l'argument original, savoir convertir le paramètre à la catégorie de valeur correspondante.

template <typename T> 
void shift(T&& t) { 
    // do nothing 
} 

template<typename T1, typename T2, typename... Ts> 
void shift(T1&& t1, T2&& t2, Ts&&... ts) { 
    std::forward<T1>(t1) = std::forward<T2>(t2); 
    shift(std::forward<T2>(t2), std::forward<Ts>(ts)...); 
} 

Ici, std::forward<T1>(t1) assure que t1 seront affectés comme lvalue si l'argument était un lvalue, et comme rvalue si l'argument était un rvalue. Par exemple, shift(42, x) ne peut pas être compilé car une valeur de type int ne peut pas être affectée à.

std::forward<T2>(t2) garantit que si l'argument pour t2 était une lvalue, il sera copié, alors que s'il s'agissait d'une valeur, il sera déplacé si possible.

std::forward<T2>(t2) et std::forward<Ts>(ts)... transmettre les informations de catégorie de valeur à l'appel récursif.

+0

Marquer celui-ci correct parce que je pense qu'il donne le meilleur/plus tôt message d'erreur lorsqu'il est mal utilisé: 'test.cpp: 14: 23: erreur: l'expression n'est pas assignable std :: avant (t1) = std :: avant (t2); – strobot

1

Vous voudriez quelque chose comme

#include <utility> 
// abort 
template <class T> void shift(T&&) { } 
// assign to lvalue 
template<class T1, class T2, class... Ts> 
void shift(T1& t1, T2&& t2, Ts&&... ts) 
{ 
    t1 = std::forward<T2>(t2); 
    shift(std::forward<T2>(t2), std::forward<Ts>(ts)...); 
} 

qui attend tous les paramètres, mais le dernier à être lvalues, où le dernier peut être une lvalue aussi.

  • Une fois que tous les paramètres sauf le dernier sont consommés, l'abandon shift(T&&) est appelé.
  • Tous les autres paramètres sont renvoyés sous la forme t1 à un point qui constitue une référence lvalue, ce qui garantit que les valeurs ont été passées dans n'importe quel emplacement, sauf le dernier.

à savoir shift(w, x, y, z, 5); mais compiles shift(w, x, y, 5, z); ou shift(w, x, y, 5, 5); ne fonctionne pas.

1

because you can't reference an rvalue.

Vous déclarer paramètre comme référence lvalue, il suffit de les changer à forwarding references, qui servent à la fois référence lvalue et référence rvalue, selon la catégorie de valeur de l'argument passé dans. par exemple.

template<typename T, typename... Ts> 
T&& first(T&& t, Ts&&... ts) 
{ 
    return std::forward<T>(t); 
} 

template<typename T> 
void shift(T&& t) 
{ 
    // do nothing 
} 

template<typename T, typename... Ts> 
void shift(T&& t, Ts&&... ts) 
{ 
    t = first(std::forward<Ts>(ts)...); 
    shift(std::forward<Ts>(ts)...); 
}