2

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.

pentadecagon
  • 4,717
  • 2
  • 18
  • 26
  • Because you can also call the second one by `f();` (not catching its return value). – herohuyongtao Mar 17 '14 at 04:23
  • 1
    My guess would be readability, especially when dealing with simultaneous implicit casts. It sounds like it would also force people to use the return value, because for `int g` and `double 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. – VoidStar Mar 17 '14 at 04:24
  • @VoidStar I do not want to overload based on different result types, just based on the presence or absence of a result type. And sometimes overloads are necessary, without e.g. overloads for `sin` you had a hard time using that function from within templates. – pentadecagon Mar 17 '14 at 04:36

5 Answers5

2

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;
}
Community
  • 1
  • 1
marcinj
  • 48,511
  • 9
  • 79
  • 100
  • 1
    This is quite cool, thanks, but it doesn't help me understand why the rules are the way they are. By now I believe the only reason is that nobody cared, probably because they don't consider it sufficiently useful. – pentadecagon Mar 17 '14 at 10:27
1

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.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • 1
    Sure, whenever there is no void overload for a function the non-void function is being called. But I want the compiler to prefer the void-function if there is one. – pentadecagon Mar 17 '14 at 04:28
0

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

Community
  • 1
  • 1
AR5HAM
  • 1,220
  • 11
  • 19
0

In 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.

herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • 1
    Name-mangling isn't standardized, compilers can do what they want, and I believe Visual Studio does it differently. Anyway, compilers *could* do it differently if overloading based on the presence of a return type were allowed. – pentadecagon Mar 17 '14 at 04:40
  • @pentadecagon Yes, it will be possible if return type is included in the name-mangling. – herohuyongtao Mar 17 '14 at 04:47
0

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.)

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • I'm pretty sure *everybody* sometimes hated the fact that `vector::pop_back` doesn't return a value, does this count as reasonably useful? – pentadecagon Mar 17 '14 at 04:43
  • @pentadecagon one reason why `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 issue – Brian Bi Mar 17 '14 at 04:47
  • @marcin_j aha, too fast for me – Brian Bi Mar 17 '14 at 04:47
  • This is a red herring. In practice nobody cares because if an assignment operator throws you are in very deep trouble anyway, and a vanished object doesn't really matter anymore. – pentadecagon Mar 17 '14 at 05:00
  • @pentadecagon well, if you're so sure, submit a proposal. – Brian Bi Mar 17 '14 at 05:09