2017-07-11 1 views
0

Dans le code ci-dessous j'ai une fonction qui modifie deux objets coûteux-à-copie, et je suis en train de sortir sans paramètres de sortiestd :: cravate et la durée de vie des objets retournés dans tuple

struct FatThing {/* some big data members here*/}; 

    auto processFatThings(FatThing ft1, FatThing ft2)-> std::tuple<FatThing, FatThing> { 
     // do smth with those two 
     return std::make_tuple(move(ft1), move(ft2)); 
    } 

    auto useProcessFatThings()-> void { 
     FatThing ft1, ft2; 
     std::tie(ft1, ft2) = processFatThings(move(ft1), move(ft2)); // dangling references? 
    } 

I Je suis un peu confus maintenant, puisque le compilateur n'émet aucun avertissement, les assainisseurs et memcheck sont propres, et le code fonctionne. Mais!!! Une référence balancée n'est-elle pas créée avec std :: tie ici?

+0

Pourquoi pensez-vous que des références pendantes sont impliquées? Vous n'utilisez aucune référence nulle part, ni dans vos arguments de fonction, ni dans vos types de retour. – pschill

Répondre

1

Non, pas de références pendantes. ft1 et ft2 seront affectés aux éléments respectifs de la valeur de retour de processFatThings

Voir le code suivant par exemple

#include <iostream> 
#include <tuple> 
#include <type_traits> 

using std::cout; 
using std::endl; 

class Something { 
public: 
    Something() { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 
    Something(Something&&) { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 
    Something(const Something&) { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 
    ~Something() { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 
    Something& operator=(const Something&) { 
     cout << __PRETTY_FUNCTION__ << endl; 
     return *this; 
    } 
}; 

int main() { 
    Something one, two; 
    std::tie(one, two) = std::make_tuple(Something(), Something()); 
} 

Démo en direct ici https://wandbox.org/permlink/iG1qIJ2VKL4bljPM

Ici, vous verrez qu'il ya 4 constructions , correspondant à one, two et les deux arguments à make_tuple. Puis, deux constructions de mouvement pour make_tuple.

Ensuite, il existe deux affectations de copie. C'est la partie clé ici. Les objets Something sont copiés sur tout ce qui était "lié" avec std::tie.

Donc, pas de références pendantes, vous obtenez des copies/mouvements!

+0

Eh bien, si vous ajoutez l'assignation de déplacement, il va l'utiliser. Je suis encore un peu confus sur comment cela fonctionne, mais je me sens beaucoup mieux maintenant :). Merci! – Slava

1

Aucune référence ne se produit ici. ft1 et ft2 sont des valeurs, et nous les lions aux références, et la durée de vie de ft1 et ft2 (qui expirent à la fin du bloc) dépasse les références (qui expirent à la fin de la ligne).

Ceci, cependant:

auto ftings = processFatThings(move(ft1), move(ft2)); 
    FatThing& ft1 = std::get<0>(ftings); 
    FatThing& ft2 = std::get<1>(ftings); 

est légèrement mieux, car il supprime une construction de mouvement. Il s'aligne également avec ce que

auto[ft1,ft2] = processFatThings(move(ft1), move(ft2)); 

fait en C++ 17.

Notez que si FatThing a de grands membres de données en il, par opposition à dont elle est propriétaire, move ne permet pas. Un réseau de 1 milliard d'éléments nécessite 1 milliard d'unités de temps pour se déplacer. Un membre de données qui est un vecteur de 1 milliard d'éléments, quant à lui, est beaucoup plus rapide à déplacer qu'à copier. (Le vecteur possède son tampon, il ne stocke pas son tampon dedans)