2010-01-08 8 views
8

À partir du code ci-dessous, Si RVO est arrivé, je me attendre à voir les 2 adresses pointant vers le même endroit, mais ce n'est pas le cas (mon compilateur est MS VC9.0)Quand RVO devrait-il intervenir?

#include <iostream> 
#include <string> 

std::string foo(std::string& s) 
{ 
    std::cout << "address: " << (unsigned int)(&s) << std::endl; 
    return s; 
} 

int main() 
{ 
    std::string base = "abc"; 
    const std::string& s = foo(base); 
    std::cout << "address: " << (unsigned int)(&s) << std::endl; 
    std::cout << s << std::endl; 
    return 0; 
} 

Dans quelles conditions L'OVR devrait-il se produire?

BTW, je fonde ma question sur la discussion suivante: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

+0

Je la question OP, il obtient une référence de ce qui est retourné par la fonction.Normalement tout le monde devrait dire "horreur, ça va planter!" mais dans ce cas le fait qu'il l'a déclaré const prolonge la durée de vie du temporaire jusqu'à la fin de la portée, lui sauvant le crash attendu. Mais dans tous les cas, il n'utilise pas de retour VALUE de toute façon à cause de cette référence. – Lightness1024

Répondre

1

Vous semblez mal comprendre RVO, essayez cet exemple (en fait NRVO):

std::string foo(const char* const s) 
{ 
    std::string out(s); 
    std::cout << "address: " << (void*)(&out) << std::endl; 
    return out; 
} 

int main() 
{ 
    std::string s = foo("abc"); 
    std::cout << "address: " << (void*)(&s) << std::endl; 
} 
+1

Je pense que vous avez tort: ​​http://codepad.org/BuTRF0Ra –

+1

Avez-vous activé les optimisations, etc. (* Propriétés du projet - C++ - Optimisation - O2 ou supérieur *)? RVO est complètement non contraignant pour le compilateur d'ailleurs. –

+2

aaah Je vois, si techniquement, quelque chose comme inline où le compilateur peut décider quand et où il veut optimiser. –

2

Je ne sais pas toutes les conditions, mais je crois que le fait que vous retourniez un paramètre et non une instance créée dans la fonction cause le problème dans votre exemple.

Pour moi, ce qui suit a montré la même adresse pour les deux:

#include <iostream> 
#include <string> 

std::string foo() 
{ 
    std::string s("rvo!"); 
    std::cout << "address: " << (void *)(&s) << std::endl; 
    return s; 
} 

int main() 
{ 
    const std::string s = foo(); 
    std::cout << "address: " << (void *)(&s) << std::endl; 
    std::cout << s << std::endl; 
    return 0; 
} 

Suivre jusqu'à ce darid commenter

Les codepad about page documents que pour elle l'utilisation par -fno-Elide-constructeurs pour C++ . La documentation de cette option forme g ++ état page man:

norme ++ Le C permet une implémentation d'omettre la création d'un temporaire qui est utilisé uniquement pour initialiser un autre objet du même type . La spécification de cette option désactive cette optimisation et oblige G ++ à appeler le constructeur de copie dans tous les cas.

Sur ma machine, la compilation avec les constructeurs -fno-elide empêche RVO, mais compile sans l'autoriser.

+2

Je pense que vous avez tort: ​​http://codepad.org/j9G52Abp –

+1

@darid - J'ai ajouté à ma réponse pour réfuter le fait que le codepad ne présente pas RVO. –

4

La réponse correcte est "chaque fois que le compilateur plaît". Un tel comportement n'est pas mandaté (mais autorisé) par le standard, et les conditions exactes dans lesquelles il intervient varient du compilateur au compilateur et de la version à la version.

En règle générale, le compilateur est plus intelligent que vous et fonctionne dans votre meilleur intérêt. Ne le remets pas en question.

Les références rvalue en C++ 0x sont en quelque sorte une version manuelle de RVO.

Editer: En regardant de plus près votre code, vous êtes définitivement en train de mal comprendre RVO. Parce que votre paramètre est une référence, il n'y a aucun moyen que la valeur de retour de la fonction puisse avoir la même adresse.

8

RVO s'applique généralement lorsque vous renvoyez un élément temporaire sans nom, mais et non si vous renvoyez un objet précédemment créé.

std::string foo() { 
    return std::string("hello world"); // RVO 
} 

std::string foo() { 
    std::string str("hello world"); 
    bar(); 
    return str; // Not RVO 
} 

std::string foo(std::string str) { 
    return str; // Not RVO 
} 

Une version plus générale est NRVO (optimisation Named valeur de retour), qui fonctionne également sur les variables nommées.

std::string foo() { 
    std::string str("hello world"); 
    bar(); 
    return str; // NRVO 
} 

std::string foo(std::string str) { 
    return str; // Not NRVO, as far as I know. The string is constructed outside the function itself, and that construction may be elided by the compiler for other reasons. 
} 

std::string foo(std::string str) { 
    std::string ret; 
    swap(ret, str); 
    return ret; // NRVO. We're returning the named variable created in the function 
} 
+1

Vous vouliez dire 'return ret', en version avec échange, bien sûr? – Tony

Questions connexes