2016-04-21 2 views
17

J'essaie de trouver un exemple simple pour une opération qui aboutit à un rvalue.Pourquoi le résultat de "decltype (i + j)" n'est-il pas une référence rvalue?

Ce cas de test aurait dû fonctionner, mais étonnamment (pour moi), le résultat de l'addition de deux int s n'est pas un rvalue (référence). Qu'est-ce que j'oublie ici?

void test(int i, int j) 
{ 
    // this assert should pass, but fails: 
    static_assert(std::is_same<decltype(i + j), int&&>(), "i + j should be a rvalue"); 
    // this assert passed, but should fail: 
    static_assert(std::is_same<decltype(i + j), int>(), "this assert should fail..."); 
} 
+0

Ce sont simplement des anciennes données. Je ne pense pas que vous pouvez std :: déplacer un int .. – 0xbaadf00d

+0

Cela a toujours été int, depuis des temps immémoriaux (a.k.a. premiers Unix). Comment déplaceriez-vous '2 + 2'? – bipll

+5

Vous pouvez beaucoup 'std :: move' un' int' et c'est d'ailleurs le point, l'OP pose une question sur la catégorie de valeur de 'i + j'. –

Répondre

33

i + j est un prvalue expression,

Une expression prvalue ("pure rvalue") est une expression qui ne possède pas l'identité et peut être déplacé à partir de. A + b, a% b, a & b, < < b, et toutes les autres expressions arithmétiques intégrées;

pas xvalue,

Un xValue ("expire value") expression est une expression qui a une identité et peut être déplacé à partir de.

Et decltype specifier cède T pour prvalue, non T&&.

a) si la catégorie de valeur de l'expression est xValue, puis cède decltype T & &;
b) si la catégorie de valeur de l'expression est lvalue, alors decltype donne T &;
c) si la catégorie de valeur de l'expression est prvalue, puis decltype donne T.

Vous pouvez le faire xvalue par std::move:

static_assert(std::is_same<decltype(std::move(i + j)), int&&>(), "std::move(i + j) is a xvalue then this static_assert won't fail"); 
+1

Le paragraphe sur 'decltype' est ce que j'ai manqué dans le cas plus général. (Ou, plus généralement, la différence entre les * catégories de valeur * et les * références auxquelles elles se lient *.) Merci! – anderas

+0

J'ai clarifié la question un peu maintenant que je sais ce que je me suis trompé. La différence est fondamentalement * est un rvalue * vs * se lie à une référence rvalue *. Je m'attendais à vérifier le second, mais fait quelque chose d'autre. Voudriez-vous ajouter cela à votre réponse, ou devrais-je écrire ma propre réponse auto-acceptée? (Puisque ce serait la solution de mon problème actuel.) – anderas

+0

@anderas Je pense que c'est bien d'écrire votre réponse, basée sur votre propre aspect et votre compréhension. – songyuanyao

5

Basé sur Answer de @ songyuanyao, je remarquai que mon erreur vérifiait la mauvaise chose: Mon intention était de vérifier si le résultat i+jse lierait à une référence rvalue, mais j'ai vérifié si est une référence rvalue.

decltype déduit du type basé sur la value category, ne se fonde pas sur ce que reference type la valeur serait se lient à:

1) si la catégorie de valeur de l'expression est xvalue, decltype alors les rendements T&&;
2) si la catégorie de valeur de l'expression est lvalue, alors decltype donne T&;
3) si la catégorie de valeur de l'expression est prvalue, alors decltype donne T.

Comme le montre la liste, depuis C++ 11, rvalues n'existent pas comme une catégorie distincte sur le niveau le plus bas. Ils sont maintenant une catégorie composite contenant à la fois prvalues ainsi que xvalues. La question telle que écrite demande si l'expression est rvalue reference et vérifie s'il s'agit d'un xvalue.

De la liste ci-dessus, il est clair que i+j est un prvalue, donc le troisième cas s'applique. Ceci explique pourquoi decltype(i + j) est int et non int&&. Les deux xvalues et prvaluesse lient à références rvalue.

Donc, en vérifiant si i+j se fixe à un lvalue reference ou rvalue reference confirme que, en effet, il se lie à un rvalue reference:

void foo(const int& f) 
{ 
    std::cout << "binds to lvalue reference" << std::endl; 
} 

void foo(int&& f) 
{ 
    std::cout << "binds to rvalue reference" << std::endl; 
} 

void test(int i, int j) 
{ 
    foo(i); // lvalue -> lvalue ref 

    foo(std::move(i)); // xvalue -> rvalue ref 
    // (std::move converts the argument to a rvalue reference and returns it as an xvalue) 

    foo(i + j); // prvalue -> rvalue ref 
} 

En conclusion:i+j est pas une référence rvalue , mais se lie à un.

+0

Pouvez-vous faire un exemple qui est capable d'aller chercher tous les trois types? – Chiel

+0

@Chiel, non, et c'est le point que j'ai essayé de faire avec la distinction entre la * catégorie * et * ce qu'une valeur lie à *: Il n'y a que des références 'rvalue' et' lvalue' (pas 'xvalue', références 'lvalue' et' prvalue'). 'prvalues' ET' xvalues' se lient aux références rvalue. – anderas

+0

@Chiel sauf si vous vouliez que je montre à quelles valeurs de ces catégories se lient. J'ai ajouté cela à l'échantillon. – anderas