Syntaxe pour références universelles

C'est une rvalue reference:

void foo(int&& a);

il ne se lie pas aux valeurs l:

int i = 42;
foo(i);   // error

il s'agit d'une référence universelle:

template<typename T>
void bar(T&& b);

il se lie aux valeurs R et se lie également aux valeurs l:

bar(i);   // okay

C'est une rvalue reference:

template<typename T>
struct X
{
    void baz(T&& c);
};

il ne se lie pas aux valeurs l:

X<int> x;
x.baz(i);   // error

pourquoi universal références utilisent la même syntaxe que les références rvalue? N'est-ce pas une source de confusion inutile? Le Comité a-t-il jamais envisagé d'autres syntaxes comme T&&& , T&* , T@ ou T&42 (je plaisante sur la dernière)? Dans l'affirmative, quelles étaient les raisons du rejet des syntaxes alternatives?

21
demandé sur M.M 2013-01-13 15:07:45

2 réponses

Une référence universelle comme T&& peut en déduire T " type d'objet " ou " type de référence "

dans votre exemple il peut déduire T comme int quand passé une valeur R, donc le paramètre de fonction est int&& , ou il peut déduire T comme int& quand passé une valeur l, auquel cas le paramètre de fonction est int& (parce que la référence les règles qui s'effondrent disent std::add_rvalue_reference<int&>::type est juste int& )

si T n'est pas déduit par l'appel de fonction (comme dans votre exemple X::baz ) alors il ne peut pas être déduit à int& , donc la référence n'est pas une référence universelle.

donc IMHO il n'y a vraiment pas besoin de nouvelle syntaxe, elle s'adapte bien dans la déduction d'argument de modèle et les règles d'effondrement de référence, avec le petit tweak qu'un paramètre de modèle peut être déduit comme référence type (où en C++03 un paramètre de modèle de fonction de type T ou T& déduirait toujours T comme un type d'objet.)

cette sémantique et cette syntaxe ont été proposées dès le début lorsque des références de valeur et un tweak aux règles de déduction d'argument ont été proposés comme solution au problème de redirection, voir N1385 . L'utilisation de cette syntaxe pour assurer une transmission parfaite a été proposée en parallèle avec la proposition R références de valeur aux fins de la sémantique des mouvements: n1377 était dans le même envoi que N1385. Je ne pense pas qu'une syntaxe alternative ait jamais été sérieusement proposée.

IMHO une syntaxe alternative serait en fait plus déroutante de toute façon. Si vous aviez template<typename T> void bar(T&@) comme syntaxe pour une référence universelle, mais la même sémantique que nous avons aujourd'hui, alors en appelant bar(i) le paramètre de modèle T pourrait être déduit comme int& ou int et le paramètre de fonction serait de type int& ou int&& ... ni l'un ni l'autre n'est T&@ " (quel que soit ce type.) Donc vous avez la grammaire dans le langage d'un déclarant T&@ qui n'est pas un type qui peut exister, parce qu'il se réfère en fait toujours à un autre type, soit int& ou int&& .

au moins avec la syntaxe nous avons le type T&& est un vrai type, et la référence s'effondre les règles ne sont pas spécifiques aux modèles de fonction utilisant des références universelles, elles sont complètement cohérentes avec le reste du système de type en dehors des modèles:

struct A {} a;
typedef A& T;
T&& ref = a;    // T&& == A&

ou l'équivalent:

struct A {} a;
typedef A& T;
std::add_rvalue_reference<T>::type ref = a;    // type == A&

quand T est un type de référence lvalue, T&& est aussi. Je ne pense pas qu'une nouvelle syntaxe soit nécessaire, les règles ne sont vraiment pas si compliquées ou déroutantes.

21
répondu Jonathan Wakely 2013-01-25 01:13:44

pourquoi les références universelles utilisent-elles la même syntaxe que les références rvalue? N'est-ce pas une source de confusion inutile? Le comité jamais envisager d'autres syntaxes...

Oui, C'est confus, IMO (Je ne suis pas d'accord avec @JonathanWakely ici). Je me souviens que lors d'une discussion informelle (déjeuner, je pense) sur le début de la conception de l'ensemble de la fonctionnalité, nous avons discuté de différentes notations (Howard Hinnant et Dave Abrahams ont été là, ils ont apporté leur idée et les gars du groupe de discussion sur la langue de travail ont donné des commentaires sur la façon dont il pourrait être adapté dans la langue de base; Ceci est antérieur à N1377). Je pense que je me souviens que &? et &|&& ont été considérés, mais tout cela était verbal; Je ne suis pas au courant que des notes de réunion ont été prises (mais je crois que C'est aussi lorsque John a suggéré l'utilisation de && pour des références de valeur). Il s'agit des premières étapes de la conception, cependant, et il y avait beaucoup de questions sémantiques fondamentales à considérer à la temps. (Par exemple, au cours du même déjeuner, nous avons également évoqué la possibilité de ne pas avoir deux types de références, mais plutôt deux types de paramètres de référence.)

un aspect plus récent de la confusion que cela provoque se trouve dans la fonctionnalité C++17 de" class template argument deduction " (P0099R3). Là, une fonction de signature de gabarit est formée en transformant la signature des constructeurs et des gabarits de constructeur. Pour quelque chose comme:

template<typename T> struct S {
  S(T&&);
};

un modèle de fonction de signature

template<typename T> auto S(T&&)->S<T>;

est formé pour utiliser pour la déduction d'une déclaration comme

int i = 42;
S s = i;  // Deduce S<int> or S<int&>?

Déduire T = int& ici serait contre-intuitif. Nous devons donc ajouter une "règle de déduction spéciale pour désactiver la règle de déduction spéciale" dans ce cas: - (

4
répondu Daveed V. 2017-01-12 13:09:47