2016-01-07 2 views
3

Je jouais avec la fonction utilitaire std :: is_same en combinaison avec la référence rvalue et lvalue et je suis tombé sur un comportement étrange. Considérons ce modèle de fonction qui vérifie le type de la variable t.std :: is_same résultat avec lvalue et référence rvalue

J'utilise VS 2013:

struct Test {}; 

template < class T> void h(T && t) 
{ 
    cout << " Is type &&: " << std::is_same<decltype(t), T &&>::value << endl; 
    cout << " Is type &: " << std::is_same<decltype(t), T &>::value << endl; 
} 

J'observe les sorties suivantes:

h(Test()); // is type && : 1 is type & : 0 

ce qui est normal pour l'instant en tant que test() est un objet temporaire, et la référence universelle dans le paramètre de h a pour valeur ar référence (& & & & = & &)

mais considérez ceci:

Test myTest; 
h(myTest); // is type && : 1 is type & : 1 !!! 

même résultat si j'écris:

Test &myTest = Test(): 
h(myTest); // is type && : 1 is type & : 1 !!! 

et même avec:

Test &&myTest = Test(): 
h(myTest); // is type && : 1 is type & : 1 !!! 

AM Je manque quelque chose? Ca ressemble à un désordre total pour moi :) Les fonctionnalités rvalue reference/decltype ne sont pas totalement supportées dans VS 2013?

Merci pour votre aide

Romain

+1

Cela me semble complètement correct. –

+0

Comment pouvez-vous être à la fois une référence lvalue et rvalue? – Romain227

+1

Indice: dans les deux derniers cas, 'T' est' Test & '. –

Répondre

4

Romain, il est assez facile. Voyons condider cas suivant:

Test myTest; 
h(myTest); // is type && : 1 is type & : 1 !!! 

intérieur h, T est Test& et t est de type Test& &&, c'est Test&. Lorsque vous effectuez le test, std::is_same<decltype(t), T &&> est vrai, car Test& && est Test& et t le type est Test&.

std::is_same<decltype(t), T &> est également vrai, puisque le type t est Test& et T& est Test&&, qui est Test&.

Une application soigneuse des règles d'effondrement de référence aide ici. Un peu sur pourquoi h() est de type Test&. La raison en est que c'est le seul type de T qui correspondra à l'argument réel. T ne peut pas être simplement Test, puisque le test & & ne liera pas (match) lvalue de type Test (car c'est le type d'argument). Toutefois, Test& && sera, en raison de règles de collapse de référence.

+0

Je ne pouvais pas comprendre deux choses pourquoi "Inside h, T est Test &" et pourquoi "Test &&, qui est Test &". Cela devrait être mon manque de connaissances sur les valeurs r. Pouvez-vous expliquer ces deux phrases s'il vous plaît? –

+0

Ouais merci, j'ai fait un bref résumé ci-dessus, mais oui, c'était essentiellement une histoire de règles de l'effondrement de référence – Romain227

+0

@KadirErdemDemir, a essayé de résoudre ce problème. – SergeyA

1

Je vais commencer ici:

template < class T> void h(T && t) 
{ 
    std::cout << " Is type &&: " << std::is_same<decltype(t), T &&>::value << '\n'; 
    std::cout << " Is type &: " << std::is_same<decltype(t), T &>::value << '\n'; 
} 

Dans le premier test, demandez-vous si decltype(t) est le même un T&&.Pour une variable, decltype(variable) est le type de déclaration de la variable. Regardez comment t est déclaré - c'est T&&.

Pas étonnamment, decltype(t) est le même que T&&, car T&& t est déclaré comme T&&.

Ceci est vrai indépendamment de ce que T est. Donc, peu importe le type que vous passez, vous obtiendrez vrai sur cette ligne.

Prenez ceci. Comprenez cela. Aucun type correspondant à T ne permet d'imprimer la première ligne 1. Si vous êtes confus au sujet de la première ligne, revenez au fait qu'il fait std::is_same< T&&, T&& >::value. Quelles que soient les règles pour ce qui est T&&, les deux côtés seront les mêmes.

Mais attendez, vous dites, n'est pas T&& une référence rvalue? Eh bien, habituellement. Si le type de type T est un type de référence, les règles de réduction de référence s'appliquent. T&& lorsque T=X& devient X&, et non X&&. La référence lvalue l'emporte sur la référence rvalue. Ceci, avec des règles de déduction spéciales, est ce que les références de transmission de pouvoirs (alias références universelles). Lorsque vous passez une lvalue de type X à h, elle en déduit T=X&. Lorsque vous transmettez une valeur de type X à h, elle en déduit T=X. Dans le premier cas, T&& devient X&, et dans la seconde, il devient X&&.

Dans votre cas concret, Test() est un rvalue de type Test, ce qui signifie h(Test())T=Test déduit, et nous obtenons T&& = Test&& tandis que T& = Test&. Le code imprime 1 puis 0, comme T&& (aka decltype(t), aka Test&&) est le même que T&&, mais pas T&.

Dans le second cas concret, h(myTest) a myTest comme lvalue (même si elle a été déclarée comme Test&&, il est une lvalue, car il a un nom). Alors T est Test&, et T&& est Test& et T& est Test&. La fonction imprime 1 puis 1, comme T&& (aka decltype(t), aka Test&) est le même que T&& et T&.

Pour résoudre votre problème, faites ceci:

template < class T> void h(T && t) 
{ 
    using rawT = std::remove_reference_t<T>; 
    std::cout << " Is type &&: " << std::is_same<decltype(t), rawT &&>::value << '\n'; 
    std::cout << " Is type &: " << std::is_same<decltype(t), rawT &>::value << '\n'; 
} 

et vous obtenez la sortie que vous attendez.