2009-05-19 9 views
3

Si je compile (sous G ++) et exécute le code suivant, il affiche "Foo :: Foo (int)". Cependant, après avoir rendu privés les opérateurs de copie et de constructeur, il échoue à se compiler avec l'erreur suivante: "error: 'Foo :: Foo (const Foo &)' est privé". Comment se fait-il qu'il a besoin d'un constructeur de copie s'il appelle seulement le constructeur standard à l'exécution?Règles d'accès du constructeur

#include <iostream> 

using namespace std; 

struct Foo { 
    Foo(int x) { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 


    Foo(const Foo& f) { 
     cout << __PRETTY_FUNCTION__ << endl; 
    } 

    Foo& operator=(const Foo& f) { 
     cout << __PRETTY_FUNCTION__ << endl; 
     return *this; 
    } 
}; 

int main() { 
    Foo f = Foo(3); 
} 

Répondre

15

Le constructeur de copie est utilisé ici:

Foo f = Foo(3); 

Cela équivaut à:

Foo f(Foo(3)); 

où le premier ensemble de parens re un appel au constructeur de copie. Vous pouvez éviter cela en disant:

Foo f(3); 

Notez que le compilateur peut choisir d'optimiser loin l'appel du constructeur de copie, mais le constructeur de copie doit toujours être disponible (i.e. non privée). Le standard C++ permet spécifiquement cette optimisation (voir section 12.8/15), peu importe ce que fait une implémentation du constructeur de copie.

+0

Si vous prétendez utiliser le constructeur de copie, pourquoi ne le voyez-vous pas dans la sortie. L'affiche originale indiquait clairement que seulement: :: Foo (int) est appelé. – KIV

+0

@Neil mais le constructeur de copie fait clairement quelque chose, il semble donc peu probable que le compilateur l'optimise ... –

+1

@Matthew: Non. La norme autorise explicitement l'optimisation de tels appels afin que le compilateur ne se soucie pas de tout Effets secondaires. Tout compilateur moderne digne de ce nom * va * optimiser cet appel. D'un autre côté, la norme stipule clairement que l'appel doit toujours être possible. Ainsi, l'explication de Neil est juste et pertinente. –

2

Ce que vous voyez, est un résultat de permis par l'optimisation standard, lorsque le compilateur évite la création de temporaire. Le compilateur est autorisé à remplacer la construction et l'assignation par une construction simple même en présence d'effets secondaires (comme les E/S dans votre exemple).

Mais le fait que le programme soit mal formé ou pas ne devrait pas dépendre de la situation, quand le compilateur fait cette optimisation ou pas. C'est pourquoi

Foo f = Foo(3); 

nécessite un constructeur de copie. Et

Foo f(3); 

ne le fait pas. Bien que cela conduira probablement au même code binaire.

Citation de 12.8.15

When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.111) This elision of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):

— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function’s return value

— when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy

Voir aussi "l'optimisation de la valeur de retour".