Question

Voici un exemple de code minimal illustrant le problème:

#include <iostream>

class Thing
{
   // Non-copyable
   Thing(const Thing&);
   Thing& operator=(const Thing&);

   int n_;

public:
   Thing(int n) : n_(n) {}

   int getValue() const { return n_;}
};

void show(const Thing& t)
{
   std::cout << t.getValue() << std::endl;
}

int main()
{
   show(3);
}

Cela génère la même erreur:

int main()
{
    show( Thing(3) );
}

Le compilateur IBM XL C / C ++ 8.0 sous AIX émet les avertissements suivants:

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed.
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed.
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible.

J'ai aussi essayé g ++ 4.1.2 avec "-Wall". et " -pedantic " et n'a pas de diagnostic. Pourquoi l'accès au constructeur de copie est-il requis ici? Comment puis-je éliminer l'avertissement, en plus de rendre l'objet copiable (ce qui est en dehors de mon contrôle) ou d'effectuer une copie explicite à transmettre (lorsque l'objet réel coûte cher à copier)?

Était-ce utile?

La solution

Les règles à cet égard figurent au § 8.5.3 / 5 de la norme. Trois situations de base sont identifiées. Les premiers impliquent que l’initialiseur ('3' dans votre cas) soit une valeur ou un type de classe. Comme aucune de celles-ci n'est vraie, vous avez le troisième cas: initialiser une référence const avec une valeur rvalue qui n'a pas de type classe. Ce cas est couvert par la dernière puce de 8.5.3 / 5:

Sinon, un temporaire de type "cv1 T1" est créé et initialisé à partir de l'expression d'initialiseur en utilisant les règles pour une initialisation de copie sans référence (8.5). La référence est alors liée au temporaire. Si T1 est lié à T2 par rapport à la référence, cv1 doit être identique à cv-qualification ou supérieur à cv-qualification par rapport à cv2; sinon, le programme est mal formé.

Edit: en relisant, je pense que IBM a raison. Je pensais auparavant à la possibilité de devoir copier le temporaire, mais ce n’est pas la source du problème. Pour créer l'initialisation temporaire en utilisant une initialisation de copie sans référence, comme spécifié au § 8.5, il faut le ctor de copie. En particulier, à ce stade, cela équivaut à une expression telle que:

T x = a;

Ceci est fondamentalement équivalent à:

T x = T (a);

I.e. il est nécessaire de créer un temporaire, puis de copier le temporaire dans l'objet en cours d'initialisation (ce qui, dans ce cas, est également un temporaire). Pour résumer le processus requis, cela équivaut à peu près au code suivant:

T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2);    // show's reference parameter binds directly to temp2

Autres conseils

C ++ permet aux compilateurs suffisamment intelligents d’éviter la copie d’objets temporaires, seule violation de la règle as-if autorisée par le standard. Je ne connais pas bien le compilateur AIX C ++ d’IBM, mais il semblerait que l’appel show (3) nécessite la copie d’un objet temporaire. Dans ce cas, C ++ requiert un constructeur de copie accessible, même si votre compilateur est suffisamment intelligent pour ne pas l'utiliser.

Mais pourquoi show (3) nécessite-t-il une copie en premier lieu? Que je n'arrive pas à comprendre. Avec un peu de chance, Litb sera bientôt là.

Mon sentiment est que answer est correct, mais il reste encore quelques questions.

Ce qui est intéressant, c’est qu’il existe un problème fondamental couvrant le paragraphe précédent de cette section ( 391 ). Cette question concerne le cas où l'argument est du même type de classe. Plus précisément:

int main () {
  show ( Thing (3) );       // not allowed under current wording
                            // but allowed with Core Issue 391

  show ( 3 );               // Still illegal with 391
}

La modification du problème fondamental 391 n'affecte que les cas dans lesquels rvalue temporaire a le même type de classe. Le libellé précédent avait:

  

Si l'expression d'initialiseur est une valeur rvalue, avec T2 un type de classe et si cv1 T1 est compatible avec cv2 T2, la référence est liée comme suit:

     

[...]

     

Le constructeur qui serait utilisé pour effectuer la copie doit pouvoir être appelé, que la copie soit réellement effectuée ou non.

Cette dernière ligne est ce qui rendrait show (Thing (3)) illégal conformément au standard actuel. La formulation proposée pour cette section est la suivante:

  

Si l'expression d'initialisation est une valeur rvalue, T2 un type de classe et "cv1 T1". est compatible avec "cv2 T2", la référence est liée à l'objet représenté par la valeur rvalue (voir 3.10 [basic.lval]) ou à un sous-objet de cet objet.

À ce stade, j’ai considéré que g ++ avait peut-être mis à jour son comportement comme indiqué dans 391 , mais que la modification a accidentellement inclus le cas d'initialisation de copie. Cependant, cela n’est pas démontré par les versions de g ++ avec lesquelles j’ai testé:

class A{
public:
  A ();
  A (int);
private:
  A (A const &);
};

void foo (A const &);

void foo ()
{
  A a = 3 ;     // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR)

  foo ( 3 ) ;   // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK)
  foo ( A() );  // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
  foo ( A(3) ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
}

Je ne peux pas trouver d'erreur dans l'interprétation de Jerry pour le cas foo (3) . Cependant, j'ai des doutes en raison de la différence entre les différents comportements du compilateur.

Que se passe-t-il si vous essayez de nommer la chose temporaire?

Thing temp (3);
show (temp);

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