2010-04-23 34 views
4

Le code suivant ne compile pas:Pourquoi le compilateur n'est-il pas plus intelligent dans ce problème de surcharge de fonction const?

#include <iostream> 
class Foo { 
    std::string s; 
public: 
    const std::string& GetString() const { return s; } 
    std::string* GetString() { return &s; } 
}; 

int main(int argc, char** argv){ 
    Foo foo; 
    const std::string& s = foo.GetString(); // error 
    return 0; 
} 

J'obtiens l'erreur suivante:

const1.cc:11: error: 
invalid initialization of reference of type 'const std::string&' 
from expression of type 'std::string* 

Il ne fait un certain sens, car foo est pas de type const Foo, mais juste Foo, de sorte que le compilateur veut utiliser la fonction non-const. Mais encore, pourquoi ne peut-il pas reconnaître que je veux appeler la fonction const GetString, en regardant la (type de) variable à laquelle je l'assigne? J'ai trouvé ce genre de surprenant.

+2

Si vous allez jeter des insultes * * autour, être préparés pour le compilateur de se demander pourquoi vous n'êtes pas assez intelligent pour définir des types de retour compatibles pour vos fonctions membres const-surcharge ;-) Si vous voulez appeler une version const d'une fonction sur un objet non-const, vous static_cast: 'const std :: string & s = static_cast (foo) .GetString();' –

+11

en fait, maintenant j'ai une idée pour une série de questions spoof stackoverflow "Pourquoi mon programmeur oublie-t-il de rendre virtuels les destructeurs de classe de base?", "Pointers: pourquoi les humains ne peuvent-ils pas les comprendre ou simplement cesser de les utiliser?", "Le code de mon humain ne compile pas, peu importe ce qu'il essaie, est-ce que j'ai un bug? etc. Probablement jamais à n'importe quoi. –

+0

@Steve: Excellente idée. N'hésitez pas à emprunter/mangle/étendre l'extrait de code dans ma réponse. –

Répondre

3

Je ne me souviens pas exactement pourquoi ils ne permettent pas de surcharger le type de retour (je pense qu'il est parce que les valeurs de retour peuvent être mis au rebut et donc la fonction ne serait pas distincte), mais vous pouvez résoudre le problème avec un soupçon const_cast au compilateur: const std::string& s = const_cast<const Foo&>(foo).GetString();

+0

Choisissez cette réponse comme réponse acceptée car elle explique en fait pourquoi la valeur de retour ne peut pas être prise en compte. – Frank

+0

I * think * c'est parce que c'était une belle propriété que le type d'une sous-expression ne dépende pas du contexte (enfin, sauf qu'il affecte la résolution de noms), juste sur la sous-expression elle-même. En fait, il y a très peu d'exceptions en C++, relatives à la sélection de la référence lorsque vous prenez un pointeur vers une fonction surchargée. –

+1

@Frank: Il n'explique pas "pourquoi la valeur de retour ne peut pas être prise en compte". Et un contre-argument évident serait que seulement si la valeur de retour est utilisée, alors il devrait être pris en compte. La seule vraie réponse est que c'est plus simple de cette façon. Incuder le type de retour dans la résolution de surcharge rendrait les choses plus compliquées. – AnT

10

Le type de retour est déterminée à partir de la fonction surchargée qui est en fait appelée, elle constitue jamais une partie de la résolution de surcharge elle-même. (Et si le type de retour n'a pas été utilisé?)

const est pas le problème avec la valeur de retour que vous pouvez lier un objet non const à une référence const, il est le fait que votre fonction retourne un pointeur que vous ne déréférenciez pas.

Comme foo n'est pas const, la non constGetString() est appelé - il est une meilleure correspondance pour un objet non const. Vous avez besoin:

const std::string& s = *foo.GetString(); 
1

Adresser MarkB's answer avec une petite illustration d'un scénario potentiellement pire que le rejet de la valeur de retour:

#include <cmath> 
#include <complex> 

struct Foo { 
    int x; 
    double y; 
    std::complex<char> z; 
    // etc, etc 
}; 

int evaluate(Foo f) { return f.x; } 
double evaluate(Foo f) { return f.y; } 
std::complex<char>(Foo f) { return f.z; } 
//etc, etc 

template <typename T> class Thingamajig 
{ 
public: 
    enum { value = sizeof (T); }; 
}; 

template <> 
class Thingamajig<double> class Thingamajig 
{ 
public: 
    int value(int a) { return a/3; } 
}; 

template <typename T> Thingamajig<T> thingamatize(const T& t) 
{ 
    return Thingajamig<T>(); 
} 

Foo myfoo = { 1, 2, 3 }; 
size_t result = sizeof(thingamatize(std::abs(4 - evaluate(myfoo))).value('B')); 

Il est un proc très long pour déterminer que double evaluate(Foo f) doit être choisi.

Lire aussi: http://blogs.msdn.com/ericlippert/archive/2006/03/31/delegates-lambdas-type-inference-and-long-playing-records.aspx

« ? Combien de travail que vous voulez créer pour votre compilateur aujourd'hui ™ »

+0

Je ne comprends pas l'exemple. La surcharge pour évaluer serait ambiguë, et vous auriez à aider le compilateur avec une distribution (si la surcharge sur le type de retour était autorisée). – UncleBens

+0

La question s'attendait à ce que le compilateur infère le type de retour en fonction du contexte. L'exemple n'est pas ambigu (si vous pensez toujours qu'il est alors listé toutes les correspondances possibles) mais très difficile à comprendre pour le compilateur. Le cas général de l'inférence de type retour en C++ est équivalent au problème d'arrêt. –

+0

Qu'en est-il de: 'size_t result = taille de (thingamatize (std :: abs (4.0f)). Value ('B'));' Il ne comprend pas qu'il devrait convertir le résultat de 'abs' en' double 'soit, donc le problème général ne semble pas être sur la surcharge en fonction du type de retour. - Votre exemple semble juste montrer que les capacités de lecture d'esprit ne peuvent pas être attendues d'un compilateur, pas une raison technique pour laquelle les types de retour * ne peuvent pas * être utilisés dans la sélection de surcharge. – UncleBens

0

Le type de retour d'une fonction n'est pas utilisé lors de la sélection de la surcharge. C'est juste comment la langue fonctionne.

Cependant, les conversions implicites sont sélectionnées en fonction du contexte. Donc, techniquement vous pouvez le faire compiler en retournant quelque chose qui est implicitement convertible à la fois à une référence et à un pointeur. Mais la vraie réponse est que les fonctions surchargées sur constness devraient avoir un type de retour similaire. (Je suppose que c'est là que la convention «utilise des pointeurs pour des choses mutables, des références const pour des choses immuables» tombe en panne.)