Is there any particular reason why C++ disallows overloading based on the presence or absence of a return-value? Like this:
void f(); //(1)
int f(); //(2)
int main(){
f(); // calls (1)
int x = f(); // calls (2)
return x;
}
The void-variant is called whenever the return-value is ignored, so there shouldn't be any problem with overload-resolution. Could be useful even in the standard library, for things like vector::pop_back(). So why is this not allowed?
Edit: It has been noted that the non-void function can be called in a void context as well. I'm aware of this. I just want the compiler to prefer the void function if there is one.
5 Answers 5
This is a language rule, in The C++ Programmin Language 4th edition, you can read:
Return types are not considered in overload resolution. The reason is to keep resolution for an individual operator (§18.2.1, §18.2.5) or function call context-independent.
but looking at your example its preety clear which function should be called:
f(); // calls (1)
int x = f(); // calls (2)
in (1) a void version, while in (2) non void version. You can read in this answer Function overloading by return type? that there are languages that allow overloading on return types.
In c++ you can achive that with some tricks, like with conversion operators, the problem is with how to call function that return void type. Below is my approach to solve this (or rather abuse language rules). I dont like the fact that void version is called in destructor - I suppose it should not be allowed to throw then. Any way this is only for fun, I would never use such code. I have compiled and run it with success on VS2005,g++4.8,clang (from http://rextester.com/runcode - not sure which version). Also I though that return value optimization would remove all the destructor calls, but it looks like it actually is not doing this.
http://coliru.stacked-crooked.com/a/6e052cc7c1bb56ca
#include<iostream>
struct SomeClass {
int nonVoidFunc() {
std::cout << "nonVoidFunc()" << std::endl;
return 0;
}
void voidFunc() {
std::cout << "voidFunc()" << std::endl;
}
};
class Proxy {
SomeClass& sc;
bool callVoid;
Proxy(Proxy& p_sc) : sc(p_sc.sc), callVoid(false) { }
Proxy& operator=(const Proxy& pr) { return *this;}
public:
Proxy(SomeClass& p_sc) : sc(p_sc), callVoid(1) {}
~Proxy() {
if ( callVoid) sc.voidFunc();
}
template<typename T> operator T() {
callVoid = false;
return sc.nonVoidFunc();
}
public:
Proxy func() { return *this; }
};
int main() {
SomeClass sc;
Proxy p1(sc);
int n = p1.func(); // prints nonVoidFunc()
(void)n;
p1.func(); // prints voidFunc()
return 0;
}
1 Comment
A function that retuns a non-void type can be called in a context where the return value is ignored. For instance, the C function printf() has a return value of type int, but it's rarely used in practice.
In other words, you can call the function int f() with:
f();
where the return value is ignored, only the side effect is used.
1 Comment
yes there is. In C++ or Java a method in any given class are recognized by the method signature, the signature here is defined as name of the method and type and number of parameters the method accepts. The return type is not part of the method signature and therefore having a method overloaded that differs only by their return type is not acceptable in these programming languages. In your example: int x = f(); you are assuming that the user will type int x however the programmer doesn't really have to do that! With the preamble I explained about method signature when the user calls f() there is no way for the compiler to recognize if you re calling the first one or the second one. That being said some modern programming languages allow overloading based on the return type. You can find a more thorough explanation here
Comments
Function declarations that differ only in the return type cannot be overloaded.
Your example with void f() and int f() is just a particular case of this rule.
In theory there's no reason I can think of for why the C++ standard couldn't make a special exception and allow overloading between void and non-void returning functions with the same parameter list with overload resolution along the same lines as what you proposed. However, it is unlikely that such an exception would be approved unless it were deemed reasonably useful...
(BTW, if you think this would be quite useful, and are prepared to defend that position, consider posting to the std-proposals mailing list.)
5 Comments
vector::pop_back doesn't return a value, does this count as reasonably useful?vector::pop_back is designed not to return the popped value is that if an exception occurs during the assignment in x = v.pop_back() then the value disappears from v entirely. Allowing this overload wouldn't address that issueIn C++, the return type of functions is not a part of the mangled name which is generated by the compiler for uniquely identifying each function. The
- No. of arguments
- Type of arguments
- Sequence of arguments
are the parameters which are used to generate the unique mangled name for each function. It is on the basis of these unique mangled names that compiler can understand which function to call even if the names are same (overloading).
Edit: It would be possible once name mangling have included the return type by the standard and at the same time not allowing to call a function without ignoring its return type.
f();(not catching its return value).int ganddouble g, calling(void)g()would be impossible for the compiler to decide what to do. Keep in mind overloads are never "necessary", they only improve readability and make it easier to remember what function to use, and if it's not going to succeed at making it more readable, then it won't be worth it for them to add.sinyou had a hard time using that function from within templates.