Question

class A
{
 public:
  A(const int n_);
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ cout << "A::operator=(const A&)" << endl; }

int foo(const A& a_)
{ return 20; }

int main()
{
  A a(foo(A(10)));    // This is line 38
  return 0;
}

L'exécution de ce code donne o / p:

  

A :: A (int), N_ = 10
  A :: A (int), N_ = 20

Il semble que le constructeur de copie est jamais appelé.

class A
{
 public:
  A(const int n_);
  A& operator=(const A& that_);
 private:
  A(const A& that_);
};

Cependant, si nous le rendre privé, cette erreur de compilation se produit:

  

Test.cpp: En fonction « int main () »:
  Test.cpp: 21: Erreur: « A :: A (const A &) » est privé
  Test.cpp: 38: erreur: dans ce contexte

Pourquoi le compilateur se plaint quand il n'utilise pas réellement le constructeur de copie?
J'utilise la version gcc 4.1.2 20.070.925 (Red Hat 4.1.2-33)

Était-ce utile?

La solution

défaut de base 391 explique la question.

En fait, la norme actuelle C ++ nécessite un constructeur de copie disponible lors du passage temporaire de type classe à une référence const.

Cette exigence sera supprimée dans C ++ 0x.

La logique nécessitant un constructeur de copie provient de ce cas:

C f();
const C& r = f(); // a copy is generated for r to refer to

Autres conseils

La norme 2003, § 12.2 / 1, déclare:

  

Même si la création du   objet temporaire est évité (12,8),   toutes les restrictions sémantiques doivent être   respecté comme si l'objet temporaire   a été créé. [Exemple: même si le   constructeur de copie n'est pas appelé, tous les   les restrictions sémantiques, telles que   l'accessibilité (article 11), est   satisfait. ]

Il existe des exemples similaires autour. D'après ce que je comprends, le compilateur est libre de générer ou de les optimiser temporaires loin.

En ce que je vois que vous n'utilisez pas le constructeur de copie partout. Dans la déclaration foo(A(10)) vous créez un objet temporaire de la classe A et en passant comme un const référence à foo. Le foo retourne un entier qui est utilisé dans la construction de a d'objet. Par conséquent, je ne vois pas où le constructeur de copie s'implique ici et comment NRVO entre en image. En outre, je compilé le code suivant en faisant privé et il a compilé le constructeur de copie bien dans VS2008.

using namespace std;

class A
{
 public:
  A(const int n_);
 private:
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ 
    cout << "A::operator=(const A&)" << endl; 
    return *this;
}

int foo(const A& a_)
{ return 20; }


int main(int argc,char *argv[])
{
   A a(foo(A(10)));    // This is line 38
  return 0;

}   

Juste une autre remarque: le compilateur fait une chose différente lorsque l'on travaille avec un temporaire. Il est donc pas le constructeur de copie, il est sur le temporaire intermédiaire.

A original(10);
foo( original ); // does compile
foo( A(10) ); // doesn't compile - needs a copy constructor

Dans l'expression:

A a(foo(A(10)));

Le résultat du A(10) sous-expression est un rvalue de type A. (5.2.3 [expr.type.conv])

Lors de l'initialisation d'une référence à partir d'un const rvalue le compilateur peut créer un temporaire dans rvalue et de se lier à ce que la référence. Même si elle choisit de ne pas, le constructeur de copie doit être accessible. (8.5.3 [decl.init.ref]) Ce ne serait pas le cas s'il y avait référence en cours d'initialisation à partir d'un compatible référence lvalue où a reçu le mandat de liaison directe.

foo prend son paramètre par référence et non la valeur, il n'y a pas de copie obligatoire pour l'initialisation de l'argument lui-même.

foo retourne un entier, donc il n'y a pas de copie d'un A ici.

a est directement initialisé à partir du int retourné par foo, donc il n'y a pas de copie de A ici.

La copie constructeur n'est pas utilisé, mais pour que le code pour compiler le constructeur de copie doivent être accessibles.

EDIT: Comeau C ++ rapports du compilateur les éléments suivants:

Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing.  All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 38: error: "A::A(const A &)" (declared at line 17), required
          for copy that was eliminated, is inaccessible
    A a(foo(A(10)));    // This is line 38
            ^

1 error detected in the compilation of "ComeauTest.c".

Notez que si les extensions C ++ 0x sont activées, il compile bien dans Comeau compilateur C ++.

En général, vous ne devriez pas vous inquiet pour si et quand le constructeur de copie est appelée. La norme C ++ est assez détendue quand les appels au constructeur de copie seront supprimés, ou pour cette matière ajouté. Si votre classe a besoin logiquement, fournir (et ne pas oublier l'opérateur destructor et l'affectation) est la règle sensible.

Lors de l'appel:

foo( A(10) );

un objet temporaire est créé au cours de la durée de vie de l'appel. Un constructeur de copie est utilisé pour alimenter les données. L'objet temporaire est supprimé après l'exécution de l'appel.

Lors de l'appel:

{ 
  A original(10);
  foo( original ); 
}

L'original est mis au rebut après la sortie du bloc. Il peut être utilisé en toute sécurité comme paramètre.

Pour une vitesse optimale, passer l'objet en référence, en utilisant une variable temporaire qui sera mis au rebut par le compilateur lors de son optimisation.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top