C ++ overload function by return type

If I thought I knew something about C ++, then you cannot overload functions with the return type.

So can someone explain what is going on here?

class A { public: typedef int _foo; }; class B {}; template<class T> typename T::_foo Foo(int) { cout << "Foo(int)\n"; return typename T::_foo(); } template<class T> typename T Foo(char) { cout << "Foo(char)\n"; return typename T(); } int main() { Foo<A>(0); // Writes "Foo(int)", as expected. Foo<B>(0); // Writes "Foo(char), expected error unable to compile template. return 0; } 

There are two classes A and B. A defines typedef _foo, B does not. There are two overloads of the function template Foo, Foo (int) and Foo (char). Foo (int) returns T :: _ foo, Foo (char) returns T.

Foo (0) is then called twice. This is an exact match for Foo (int), so I would expect Foo <A> (0) to compile ok, and Foo <B> (0) not to compile, since B does not determine the type of _foo used in the template.

What actually happens is that Foo <B> (0) completely ignores Foo (int) and instead creates Foo (char). But by normal overload resolution rules, Foo (0) is clearly an exact match for Foo (int), and the only thing that makes Foo (char) more viable is the return type, which should not be considered.

To make sure this is a return value that affects overload resolution, just add this:

 template<class T> void Bar(int) { typename T::_foo a; cout << "Bar(int)\n"; } template<class T> void Bar(char) { cout << "Bar(char)\n"; } Bar<A>(0); // Writes "Bar(int), as expected. //Bar<B>(0); // Error C2039: '_foo' : is not a member of 'B', as expected. 

This makes it clear that in the absence of a return value, Foo (int) is indeed the correct overload, and that if the template cannot resolve the types used from its template argument, then refusing compilation is a normal result.

+6
source share
2 answers

You do not overload the return type, you specialize in the function template, and when the specialization Foo<B>(int) forms an invalid type B::_foo , which specialization is removed from the overload set by SFINAE, leaving the function Foo<B>(char) as the only one viable function.

In more detail, calling Foo<A>(0) first searches for the name to find all Foo names in scope, then instantiates any function templates to search for overload candidates, and then overload resolution selects the best match.

The function template creation step creates these two function declarations:

 int Foo<A>(int); A Foo<A>(char); 

Overload resolution selects the first one as the best.

However, when calling Foo<B>(0) instances create these declarations:

 <invalid type> Foo<B>(int); B Foo<B>(char); 

The first declaration is not valid, so there is only one candidate for permission to overload, so this is the one that is being called.

In your Bar example, the invalid type that is generated during the instantiation is not in the "direct context" of the function declaration (this is in the definition of the ie body function), so SFINAE is not applied.

+7
source
 template<class T> typename T::_foo Foo(int); template<class T> typename T Foo(char); 

So your code declares this overloaded function. It's nice.

 Foo<A>(0); 

In this case, the compiler tries to fill out the template for the prototypes mentioned above:

 int Foo(int); A foo(char); 

And since you are passing an integer as a parameter, the first is a better match, so the compiler uses this.

 Foo<B>(0); 

Again, the compiler sees this line and tries to fill out a prototype template, but ...

 WTFDOESNTMAKESENSE?!?!? Foo(int); A foo(char); 

It is so clear that the first one does not even make sense, so he discards it and uses the second overload. This actually has nothing to do with the types of data returned; it is related to how the template templates are filled before it decides which function you have in mind. Here's your example rebuilt to clarify:

 template<class T> int foo(T::_foo) {} template<class T> int foo(char) {} int main() { foo<A>(0); //uses the first, `int foo(int)` better than `int foo(char)` foo<B>(0); //uses the second, because the first doesn't work with B. 

This is called SFINAE, and note that it only works in special circumstances in template parameters, return types, and function parameters, but not in the function body itself. That's why your β€œcheck” caused an error because it cannot say that one of the functions is not valid from the prototype, and the prototype is the only thing that counts when it decides between overloads.

+5
source

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


All Articles