2010-05-05 3 views
11
class Foo 
{ 
public: 
    explicit Foo() {} 
    explicit Foo(Foo&) {} 
}; 

Foo d = Foo(); 

error: no matching function for call to 'Foo::Foo(Foo)'Foo f = Foo(); // aucune fonction correspondante pour l'appel à 'Foo :: Foo (Foo)' ... hein?

J'ai essayé de changer Foo(Foo&)-Foo(Foo) comme l'erreur suggère, qui est AFAIK pas un constructeur valide, et bien sûr je reçois:

error: invalid constructor; you probably meant ‘Foo (const Foo&)’

Qu'est-ce qui se passe? Comment résoudre ça? (Ceci est sur GCC au fait)

+1

Foo Foo Foo? Huh? –

+2

Le compilateur a déjà répondu à votre question ... 'Foo (const Foo &)'. 'Foo d = Foo();' appelle le constructeur de la copie. –

+3

+1 parce que personne ne semble connaître la réponse –

Répondre

12

Vous avez deux points douteux dans votre constructeur de copie.

D'abord, vous avez fait le explicite constructeur de copie (ce qui est une chose douteuse à faire), donc vous (en théorie) doivent faire:

Foo d((Foo())); 

Deuxièmement, votre constructeur de copie prend référence et non une référence const ce qui signifie que vous ne pouvez pas l'utiliser avec un Foo temporaire.

Personnellement, je voudrais simplement supprimer explicit du constructeur de copie et lui faire prendre une référence const si possible.

Notez que le explicit sur votre constructeur par défaut n'a aucun effet. [*] explicit a seulement un effet sur les constructeurs qui peuvent être appelés avec un seul paramètre. Cela les empêche d'être utilisés pour des conversions implicites. Pour les constructeurs qui prennent seulement zéro ou seulement deux paramètres ou plus, cela n'a aucun effet.

[Note: il peut y avoir une différence entre:.

Foo d; 

et

Foo d = Foo(); 

mais dans ce cas, vous avez un constructeur par défaut déclarée par l'utilisateur si cela ne vaut pas]

Modifier: [*] Je viens de double vérifier cela et 12.3.1 [class.conv.ctor] dit que vous pouvez faire par défaut contre tructor explicit. Dans ce cas, le constructeur sera utilisé pour initialisation par défaut ou initialisation de la valeur.Pour être honnête, je ne comprends pas la valeur de ceci comme si vous aviez un constructeur déclaré par l'utilisateur alors c'est un type non-POD et même les objets locaux de type non-POD sont initialisés par défaut s'ils n'ont pas d'initialiseur que cette clause dit peut être fait par un constructeur par défaut explicit. Peut-être que quelqu'un peut signaler un cas de coin où cela fait une différence, mais pour l'instant je ne vois pas quel effet explicit a sur un constructeur par défaut.

+1

Pourquoi l'ensemble supplémentaire de parenthèses? – clahey

+2

@clahey: Pour contourner l'analyse la plus vexante. –

+4

Maintenant que James a donné la bonne réponse, je peux juste dire: parce que je suis un fan de lisp placard. –

2

Votre problème est dans l'instanciation. Vous n'avez pas besoin de Foo d = Foo(); pour un constructeur par défaut.

Gardez votre classe même, mais essayez ceci pour instanciation:

Foo d; 

En fait, vous ne même pas besoin Foo d = Foo(arguments); pour la construction avec des paramètres. Cela devrait être comme ceci:

Foo d(arguments); 
+0

'Foo d = Foo()' était juste une simplification pour générer l'erreur. J'aurais pu avoir 'Foo d;' et 'Foo k = d;' ailleurs – Kyle

+0

@Kyle: Parmi les nombreux commentaires que vous avez maintenant laissés, je peux voir que vous essayiez délibérément d'invoquer le constructeur de copie. J'aimerais que vous l'ayez dit explicitement dans votre question. Je ne trouve nulle part dans votre question que vous impliquez que vous savez même ce qu'est un constructeur de copie *; vous venez de poster du code et vous avez dit "pourquoi est-ce que ça brise?" J'ai fait l'hypothèse (ouais, ouais) que vous faisiez tout simplement mal. 'Foo d = Foo();' invoque certainement le constructeur de copie sur cette instance fugitive de 'Foo', mais c'est quelque chose que vous ne devriez jamais * faire *. – Randolpho

+0

Comme implicite dans son titre, ma question concerne le message d'erreur bizarre, qui ne semble pas être un résultat évident du code donné. Je dois encore maîtriser l'habileté de prendre une question si précisément que personne ne peut répondre à la mauvaise question. S'il vous plaît modifier votre réponse SO donc me permettra de vous upvote. – Kyle

2
Foo d = Foo(); 

devrait être

Foo d; 

La première ligne crée une instance Foo puis recopié à d;

+0

Correct, mais pas une réponse à la question - 'Foo d = Foo()' était juste une simplification pour générer l'erreur. J'aurais pu avoir 'Foo d 'et' Foo k = d; 'ailleurs. – Kyle

3

Essayer sans l'aide de l'explicite? Je pense que:

Foo foo = Foo() 

crée une copie implicite, ainsi le constructeur de copie explicite n'est pas déclenché.

Edit:

Ceci est seulement la moitié de la réponse. Voir Charles Bailey ou UncleBens pour savoir pourquoi const est nécessaire.

+1

Ceci est la bonne réponse. En outre, il n'y a aucune raison de rendre le constructeur de la copie explicite pour commencer. Les constructeurs explicites sont destinés à vous empêcher de convertir accidentellement des types implicitement. –

+1

Ceci est la moitié de la réponse. – clahey

1

Le compilateur vous dit ... Utilisez ceci:

Foo(const Foo&) {} 
+0

Contre-voté. C'est littéralement dire à quoi devrait ressembler la signature. (Pas ce qu'il devrait faire - rien - cependant.) – UncleBens

5

Vous ne voulez pas marquer l'un de ces constructeurs comme explicite - le compilateur doit utiliser les deux d'entre eux, en particulier le constructeur de copie, implicitement. Qu'est-ce que vous essayez d'atteindre en les marquant explicitement?

+0

Pour répondre à votre question: Je veux éliminer toutes les surprises causées par une conversion que je ne voulais pas avoir (... en demandant au compilateur d'élever une erreur). – Kyle

+1

@Kyle Mais vous voulez que le compilateur soit capable de faire des copies - ce n'est pas une conversion. Et aucune n'est la construction par défaut. –

+0

Bien, mais je veux que ça bombe si jamais j'essaie de faire 'Foo f = Bar()' où 'Bar' se laisse heureusement convertir en Foo en fournissant un' Foo opérateur() '(je pense que j'ai ce processus de pensée à droite ..) – Kyle

0

Vous pouvez résoudre le problème de deux manières. Un (déjà suggéré par Randolpho) est d'éliminer l'utilisation de la copie ctor. L'autre est d'écrire une copie correcte ctor:

Foo (Foo const &) {} 

Vous voulez généralement faire les deux.

Editer: En le regardant, mon dernier commentaire est facile à interpréter. Beaucoup de classes ont besoin d'une copie ctor du tout, mais si vous avez besoin d'une copie ctor, il devrait normalement avoir la forme ci-dessus (pas explicite, et en prenant une référence const comme paramètre).

+0

'Foo (Foo &)' est un constructeur de copie approprié. Ce n'est tout simplement pas le bon constructeur de copie pour ce qu'il essaie de faire. –

+0

@Dennis: Si vous l'aimez, c'est bien - mais un constructeur de copie qui ouvre la porte à la modification de l'objet en cours de copie n'est pas ce que j'appellerais «correct». –

-1
class Foo 
{ 
public: 
    explicit Foo() {} 
    explicit Foo(const Foo&) {} 
}; 

Foo d = Foo() 
+0

pourquoi négatif 1? – Betamoo

+0

ce n'était pas moi, mais je peux vous dire que ne compile toujours pas. – Kyle

3

Un constructeur de copie ne doit pas être explicite (ce qui le rend uncallable ici et dans bien d'autres contextes parfaitement raisonnables, comme lors du passage ou le retour en valeur).

Ensuite, il devrait prendre l'argument par const référence, car sinon il ne peut pas se lier à des temporaires.

Foo f = Foo(); 
     ^^^^^ 
      | 
      --- this is a temporary that cannot be passed to a function 
       that accepts a non-const reference 

De plus, il n'y a aucune raison de faire le constructeur par défaut explicite: ce mot-clé n'a de sens que pour les constructeurs (autres que le constructeur de copie) qui peut être appelée avec un seul argument, auquel cas il empêche conversions implicites d'autres types dans Foo via ce constructeur.Par exemple, si un constructeur prend un int étaient explicites, des situations comme celles-ci ne seraient pas compiler:

Foo f; 
f = 1; //assuming no operator= overload for (types convertible from) int 
     //this implicitly performs f = Foo(1); 

Foo g = 10; 

void x(Foo); 
x(20); 

Au total:

class Foo 
{ 
public: 
    Foo(); 
    Foo(const Foo&); 
    //... 
}; 

Foo x = Foo(); 

Et d'ailleurs, si aucun de ces constructeurs est destiné à faire n'importe quoi, vous n'avez pas besoin de les définir du tout - le compilateur les fournira automatiquement (si vous définissez d'autres constructeurs, le constructeur par défaut ne sera pas généré automatiquement, cependant).

+0

C'est la meilleure réponse jusqu'à présent, mais je voudrais ajouter plus de texte sur ce que la bonne chose à faire est. – clahey

+0

Vous pouvez appeler un constructeur de copie explicite à l'aide de l'initialisation directe (par exemple, 'Foo f; Foo g (f);'). –

+0

@James: Merci, a ajouté une clarification. – UncleBens

4

D'abord, ni le constructeur par défaut ni le constructeur de copie ne doivent jamais être explicit. Vous avez seulement besoin de créer un constructeur explicit s'il prend un seul argument d'un autre type, pour empêcher la conversion implicite de ce type. Le constructeur de copie prend une référence à la classe elle-même, il n'y a donc pas de risque de conversion indésirable. Ensuite, assurez-vous que le constructeur de copie prend une référence const.

Troisièmement, Foo f; est le bon moyen d'avoir un objet construit par défaut de classe foo. Notez que Foo f(); est faux, car le compilateur l'interprétera comme une déclaration de la fonction f() qui renvoie un objet de la classe Foo. Quatrièmement, si vous avez écrit votre propre constructeur de copie, vous devez également écrire l'opérateur d'affectation.

 

class Foo 
{ 
    Foo() {} // no need to make explicit. Nothing to convert from. 

    Foo(const &Foo f) {} // again, nothing wrong with conversion from Foo to Foo 

    explicit Foo(int a) {} // need explicit to prevent accidental passing of an int 
          // to a function that takes Foo as an argument 
}; 
 
Questions connexes