Why isn't the compiler smarter about the const function overload problem?

The following code does not compile:

#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; } 

I get the following error:

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

This makes sense because foo not of type const Foo , but just foo , so the compiler wants to use the non-const function. But still, why can't he recognize that I want to call the const GetString function by looking at the variable (type) that I assigned to it? I found this amazing.

+4
source share
4 answers

I can’t remember exactly why they do not allow overloading by return type (I think this is because the return values ​​can be discarded and, therefore, the function will not be different), but you can fix the problem using the const_cast hint to the compiler: const std::string& s = const_cast<const Foo&>(foo).GetString();

+3
source

The return type is determined from the overloaded function that is actually called; it never forms part of the overload resolution itself. (What if the return type was not used?)

const not a problem with the return value, since you can bind a const object to a const reference, it is a fact that your function returns a pointer that you are not looking for.

Since foo not const , it is not const GetString() called - this is the best match for a const object. You need:

 const std::string& s = *foo.GetString(); 
+10
source

To expand MarkB's answer with a small illustration of a potentially worse scenario than dropping the return value:

 #include <cmath> #include <complex> struct Foo { int x; double y; std::complex<char> z; // etc, etc }; int evaluate(Foo f) { return fx; } double evaluate(Foo f) { return fy; } std::complex<char>(Foo f) { return fz; } //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')); 

A very long process to determine what double evaluate(Foo f) should be chosen.

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

"How much work do you want to create for your compiler today? & Trade;"

+1
source

The return type of the function is not used when selecting overload. This is how the language works.

However, implicit conversions are selected based on context. Thus, technically you can compile it by returning something that is implicitly converted to both a link and a pointer.

 #include <iostream> #include <string> struct Evil { std::string* p; Evil(std::string* p): p(p) {} operator std::string*() const { return p; } operator std::string&() const { return *p; } }; struct ConstEvil { const std::string* p; ConstEvil(const std::string* p): p(p) {} operator const std::string*() const { return p; } operator const std::string&() const { return *p; } }; class Foo { std::string s; public: ConstEvil GetString() const { return ConstEvil(&s); } Evil GetString() { return Evil(&s); } }; int main(int argc, char** argv){ Foo foo; const std::string& s = foo.GetString(); // ok return 0; } 

But the real answer is that functions overloaded in constness should have a similar return type. (I suppose this means that the convention "uses pointers to mutable things, links to constant links" just breaks down.)

0
source

Source: https://habr.com/ru/post/1307846/


All Articles