916 votes

Ce qui ne T&& (double esperluette) signifie en C++11?

J'ai été regarder dans certaines des nouvelles fonctionnalités de C++11 et que j'ai remarqué, c'est la double esperluette dans la déclaration des variables, comme T&& var.

Pour commencer, qu'est-ce que cette bête a appelé? Je souhaite que Google nous permettrait de rechercher des signes de ponctuation comme ça.

Qu'est ce qu'il signifie?

À première vue, il semble être un double de référence (comme le C-double de style des pointeurs T** var), mais je vais avoir un moment difficile de penser à un cas d'utilisation.

744voto

Peter Huene Points 4454

Elle déclare une référence rvalue (proposition de normes doc).

Voici une introduction à la rvalue references: http://www.artima.com/cppsource/rvalue.html.
Voici un fantastique regard en profondeur sur les références rvalue par l'un de Microsoft de la bibliothèque standard, les développeurs: http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx.

La plus grande différence entre un C++03 référence (maintenant appelé une lvalue de référence en C++0x), c'est qu'il peut se lier à une rvalue temporaire sans avoir à être const. Ainsi, cette syntaxe est maintenant légal:

T&& r = T();

références rvalue principalement les suivantes:

La sémantique de déplacement. Un constructeur de déplacement et d'assignation de déplacement de l'opérateur peut désormais être défini qui prend une référence rvalue au lieu de l'habituel const-lvalue de référence. Un déplacement des fonctions comme une copie, sauf qu'il n'est pas obligé de garder la source inchangée; en fait, il modifie la source de sorte qu'il ne possède plus le déplacé ressources. C'est excellent pour éliminer les copies superflues, en particulier dans les implémentations de la bibliothèque standard.

Par exemple, un constructeur de copie pourrait ressembler à ceci:

foo(foo const& other)
{
    this->length = other.length;
    this->ptr = new int[other.length];
    copy(other.ptr, other.ptr + other.length, this->ptr);
}

Si ce constructeur a été adoptée temporaire, la copie serait inutile parce que nous savons temporaire sera détruit; pourquoi ne pas utiliser les ressources temporaires déjà alloué? En C++03, il n'y a aucun moyen d'empêcher la copie que nous ne pouvons pas déterminer nous avons passé un temporaire. Dans C++0x, on peut surcharger un constructeur de déplacement:

foo(foo&& other)
{
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;
}

Avis de la grande différence ici: le constructeur de déplacement modifie en fait son argument. Cela permettrait effectivement de "déplacer" le temporaire de l'objet en cours de construction, ce qui élimine la copie superflue.

Le constructeur de déplacement serait utilisé pour les temporaires et pour les non-const lvalue références qui sont explicitement convertis aux références rvalue à l'aide de l' std::move de la fonction (il vient effectue la conversion). Le code suivant à la fois invoquer le constructeur de déplacement pour f1 et f2:

foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"

Le transfert parfait. références rvalue nous permettre de bien des arguments pour templated fonctions. Prenez l'exemple de cette usine de la fonction:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
    return std::unique_ptr<T>(new T(a1));
}

Si nous avons appelé usine<toto>(5), l'argument va être déduites à int&, qui ne pourront pas se lier à un littéral 5, même si foo constructeur prend un int. Eh bien, on pourrait plutôt utiliser A1 const&, mais que faire si foo prend l'argument du constructeur par des non-const de référence? Pour faire un véritable générique de l'usine de fonction, nous aurions à la surcharge de l'usine sur A1& et A1 const&. Qui pourrait être bien si l'usine prend 1 type de paramètre, mais chaque type de paramètre permettrait de multiplier la surcharge nécessaire de définir par 2. C'est très rapidement désuète.

références rvalue résoudre ce problème en permettant à la bibliothèque standard de définir un std::forward fonction qui peut correctement le lvalue/références rvalue. Pour plus d'informations sur comment std::forward , voir cette excellente réponse.

Cela nous permet de définir l'usine de la fonction comme ceci:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
    return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}

Maintenant l'argument de la rvalue/lvalue-ness est préservée lorsqu'il est passé à T du constructeur. Cela signifie que si l'usine est appelée avec une valeur r, T du constructeur est appelé avec une rvalue. Si l'usine est appelée avec une lvalue, des T-constructeur est appelé avec une lvalue. L'amélioration de la fonction de fabrication des œuvres, car d'une règle spéciale:

Lorsque le paramètre de la fonction est du type de la forme de la T&& où T est un modèle paramètre, et l'argument de la fonction est une lvalue de type A, de type A et est utilisé pour l'argument de modèle déduction

Ainsi, nous pouvons utiliser l'usine comme suit:

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)

Important référence rvalue propriétés:

  • Pour la résolution de surcharge, lvalues préfèrent la liaison lvalue des références et des rvalues préfèrent la liaison références rvalue. Donc pourquoi temporaires préfèrent invoquer un constructeur de déplacement / opérateur d'assignation de déplacement de plus d'un constructeur de copie / opérateur d'affectation.
  • références rvalue sera implicitement se lier à des rvalues et temporaires qui sont le résultat d'une conversion implicite. c'est à dire float f = 0f; int&& i = f; est bien formée, car le flotteur est implicitement converti en int; la référence à une temporaire qui est le résultat de la conversion.
  • Nommé références rvalue sont lvalues. Sans nom rvalue references sont rvalues. C'est important de comprendre pourquoi l' std::move appel est nécessaire: foo&& r = foo(); foo f = std::move(r);

89voto

Puppy Points 90818

Elle indique une référence rvalue. Références Rvalue ne se lier à des objets temporaires, sauf s'il est explicitement généré le contraire. Ils sont utilisés pour fabriquer des objets beaucoup plus efficace dans certaines circonstances, et de fournir des installations connu comme le transfert parfait, ce qui simplifie grandement le code du modèle.

En C++03, vous ne pouvez pas distinguer entre, d'une copie d'un non-mutable lvalue et une rvalue.

std::string s;
std::string another(s);           // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(const std::string&);

Dans C++0x, ce n'est pas le cas.

std::string s;
std::string another(s);           // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(std::string&&);

Envisager la mise en œuvre derrière ces constructeurs. Dans le premier cas, la chaîne a pour effectuer une copie de maintenir la valeur de la sémantique, ce qui implique une nouvelle allocation de tas. Cependant, dans le second cas, nous savons à l'avance que l'objet qui a été transmis à notre constructeur est immédiatement en raison de la destruction, et il ne doit rester intacte. Nous pouvons effectivement juste changer les pointeurs internes et de ne pas effectuer toute copie à tous dans ce scénario, ce qui est nettement plus efficace. La sémantique de déplacement bénéficier à toute la classe qui est cher ou interdit la copie de l'interne les ressources référencées. Prenons le cas de l' std::unique_ptr- maintenant que notre de classe peut distinguer temporaires et non temporaires, nous pouvons faire de la sémantique de déplacement de travailler correctement, de sorte que l' unique_ptr ne peut pas être copié, mais peut être déplacé, ce qui signifie qu' std::unique_ptr peut légalement être stockés dans des conteneurs Standard, triés, etc, alors que C++03 de l' std::auto_ptr ne le peuvent pas.

Maintenant, nous considérons l'autre utilisation de références rvalue - transfert parfait. Examiner la question de la liaison d'une référence à une référence.

std::string s;
std::string& ref = s;
(std::string&)& anotherref = ref; // usually expressed via template

Ne pouvez pas rappeler ce que C++03) dit à ce sujet, mais en C++0x, la résultante de type lorsque vous traitez avec des références rvalue est critique. Une rvalue référence à un type T, où T est un type référence, devient une référence de type T.

(std::string&)&& ref // ref is std::string&
(const std::string&)&& ref // ref is const std::string&
(std::string&&)&& ref // ref is std::string&&
(const std::string&&)&& ref // ref is const std::string&&

Considérer le simple modèle de la fonction min et max. En C++03 vous avez une surcharge pour les quatre combinaisons de const et non const manuellement. Dans C++0x, c'est juste une surcharge. Combiné avec les variadic templates, ce qui permet le transfert parfait.

template<typename A, typename B> auto min(A&& aref, B&& bref) {
    // for example, if you pass a const std::string& as first argument,
    // then A becomes const std::string& and by extension, aref becomes
    // const std::string&, completely maintaining it's type information.
    if (std::forward<A>(aref) < std::forward<B>(bref))
        return std::forward<A>(aref);
    else
        return std::forward<B>(bref);
}

J'ai laissé le type de retour de la déduction, parce que je ne peux pas me rappeler comment c'est fait désinvolte, mais que min peut accepter n'importe quelle combinaison des lvalues, rvalues, const lvalues.

26voto

mmocny Points 3655

Le terme de T&& lorsqu'il est utilisé avec le type de déduction (comme pour le transfert parfait) est connu familièrement comme une référence universelle. Cela a été inventé par Scott Meyers dans cet article.

C'est parce qu'elle peut être soit la valeur r ou l-valeur.

Des exemples sont:

// template
template<class T> foo(T&& t) { ... }

// auto
auto&& t = ...;

// typedef
typedef ... T;
T&& t = ...;

// decltype
decltype(...)&& t = ...;

Notez que la norme elle-même n'a aucune notion de ce fait, il est tout simplement un moyen de discuter de l' (bizarre?) la combinaison de la référence de l'effondrement des règles, le type de référence de la déduction, et l' && syntaxe.

Plus la discussion peut être trouvé dans la réponse pour: Syntaxe des références universelles

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X