33 votes

Est-ce que cette utilisation correcte de la sémantique 'move' du C ++?

Ce soir, j'ai été jeter un oeil à certaines de code que j'ai travaillé au cours des derniers jours, et a commencé à lire sur la sémantique de déplacement, plus précisément std::move. J'ai quelques questions à vous poser des pros pour s'assurer que je suis sur la bonne voie et de ne pas prendre de stupide hypothèses!

Tout d'abord:

1) a l'Origine, mon code a une fonction qui a donné un grand vecteur:

template<class T> class MyObject
{
public:
    std::vector<T> doSomething() const;
    {
        std::vector<T> theVector;

        // produce/work with a vector right here

        return(theVector);
    }; // eo doSomething
};  // eo class MyObject

Compte tenu de "theVector" est temporaire dans cette et "jeter", j'ai modifié la fonction de:

    std::vector<T>&& doSomething() const;
    {
        std::vector<T> theVector;

        // produce/work with a vector right here

        return(static_cast<std::vector<T>&&>(theVector));
    }; // eo doSomething

Est-ce correct? Tout pièges en faisant de cette façon?

2) j'ai remarqué dans une fonction que j'ai que des retours std::string automatiquement appelé le constructeur de déplacement. Le débogage dans le Retour de la Chaîne (merci, Aragorn), j'ai remarqué qu'il appelle explicitement un constructeur de déplacement. Pourquoi est-il un pour la classe string et pas de vecteur?

Je n'ai pas à apporter des modifications à cette fonction pour prendre avantage de la sémantique de déplacement:

// below, no need for std::string&& return value?
std::string AnyConverter::toString(const boost::any& _val) const
{
    string ret;
    // convert here
    return(ret); // No need for static_cast<std::string&&> ?
}; // eo toString

3) Enfin, je voulais faire des tests de performance, est le très-rapide des résultats que j'ai obtenu en raison de std::move sémantique ou fait de mon compilateur (VS2010) faire une optimisation de trop?

(Mise en œuvre de l' _getMilliseconds() omis par souci de concision)

std::vector<int> v;
for(int a(0); a < 1000000; ++a)
    v.push_back(a);

std::vector<int> x;
for(int a(0); a < 1000000; ++a)
    x.push_back(a);

    int s1 = _getMilliseconds();
std::vector<int> v2 = v;
    int s2 =  _getMilliseconds();
std::vector<int> v3 = std::move(x);
    int s3 =  _getMilliseconds();

    int result1 = s2 - s1;
    int result2 = s3 - s2;

Les résultats ont été, de toute évidence, génial. result1, d'une affectation standard, a pris 630ms. Le deuxième résultat, a été 0ms. Est-ce un bon test de performance de ces choses?

Je sais que certains de ce qui est une évidence pour beaucoup d'entre vous, mais je veux m'assurer de comprendre la sémantique juste avant d'aller me blazer sur mon code.

Merci à l'avance!

36voto

GManNickG Points 155079

Une référence est toujours une référence. De la même manière vous ne pouvez pas retourner une référence à un local en C++03 (ou UB), vous ne pouvez pas en C++0x. Vous vous retrouverez avec une référence à un objet mort; il arrive juste à être une référence rvalue. Donc, c'est faux:

std::vector<T>&& doSomething() const
{
    std::vector<T> local;

    return local; // oops
    return std::move(local); // also oops
}

Vous devriez faire ce que vous avez vu au nombre de deux:

// okay, return by-value 
std::vector<T> doSomething() const
{
    std::vector<T> local;

    return local; // exactly the same as:
    return std::move(local); // move-construct value
}

Les Variables locales à une fonction sont temporaires lorsque vous revenez, il n'ya donc pas besoin de modifier votre code. Le type de retour est la chose responsable de la mise en œuvre de la sémantique de déplacement, pas vous.

Vous souhaitez utiliser std::move aux explicitement déplacer quelque chose, quand il ne serait pas fait normalement, comme dans votre test. (Ce qui semble être beaux; que dans la Presse? Vous devez afficher le contenu du vecteur, ou le compilateur d'optimiser loin.)

Si vous voulez vous renseigner sur les références rvalue, lisez ceci.

14voto

FredOverflow Points 88201
return(theVector);

Déjà se déplace implicitement en raison d'un langage spécial de la règle, parce qu' theVector est un objet local. Voir la section 12.8 paragraphes 34 et 35:

Lorsque certains critères sont respectés, une mise en œuvre est permis d'omettre de le copier/déplacer construction d'une classe objet, même si le copier/déplacer constructeur et/ou le destructeur de l'objet ont des effets secondaires. Dans de tels cas, la mise en œuvre traite de la source et de la cible de l'omis de copier/déplacer des fonctionnement que simplement deux manières de se référer au même objet, et la destruction de cet objet se produit au plus tard à la fois lorsque les deux objets ont été détruits sans l'optimisation. Cette élision de copier/déplacer opérations, appelées copie élision, est autorisé dans les circonstances suivantes (qui peuvent être combinées pour éliminer plusieurs copies):

- dans une instruction return dans une fonction avec une classe de type de retour, lorsque l'expression est le nom de un non-volatile automatique des objets avec le même cv-non qualifiés de type que le type de retour de fonction, la copier/déplacer opération peut être omis, par la construction de l'automatique des objets directement dans la fonction valeur de retour

[...]

Lorsque les critères d'élision d'une opération de copie sont remplies et que l'objet à copier est désigné par un lvalue, résolution de surcharge pour sélectionner le constructeur de copie est effectuée que si l'objet ont été désigné par une rvalue.

Notez que vous devez retourner un std::vector<T> (en valeur), pas un std::vector<T>&& (par référence).

Mais pourquoi la parenthèse? return n'est pas une fonction:

return theVector;

7voto

usta Points 4536

Pour ajouter à GMan de réponse: même si vous modifiez le type de retour de std::vector<T> (sans références, sinon vous obtiendrez UB), votre changement d'expression de renvoi à "1)" jamais faire le mieux, mais pourrait le rendre un peu pire. En tant que std::vector a constructeur de déplacement, et de vous renvoyer à un objet local, vectors'constructeur de copie va pas être appelé, peu importe que vous avez écrit, return theVector;, return static_cast<std::vector<T>&&>(theVector);ou return std::move(theVector). Dans les deux derniers cas, le compilateur va être obligé d'appeler le constructeur de déplacement. Mais dans le premier cas, on a la liberté à l'optimisation de la déplacer au total, si l'on peut faire NRVO pour cette fonction. Si NRVO n'est pas possible pour une raison quelconque, alors seulement le compilateur station d'appeler le constructeur de déplacement. Afin de ne pas changer l' return x; de return std::move(x); si x est une organisation non-statique de l'objet dans la fonction retourné à partir de, sinon vous allez prévenir le compilateur d'utiliser une autre optimisation occasion.

5voto

AshleysBrain Points 11439

La façon standard de déplacer quelque chose avec std::move(x), pas un static_cast. Autant que je sache, le Nommé Valeur de Retour d'Optimisation est probablement à coup de pied avec le retour d'un vecteur par la valeur, il aurait fait bien avant la sémantique de déplacement ainsi.

Votre test de performance en est une bonne illustration de la façon dont la sémantique de déplacement est bon pour la performance: le copier-cession a copier un million d'éléments, ainsi que de la cession essentiellement des swaps le vecteur de pointeurs internes, ce qui est un vain mot d'affectation ou deux, il suffit de quelques cycles.

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