19

If we have this example functions code in C++

void foo(int x)  { std::cout << "foo(int)"   << std::endl; }
void foo(int& x) { std::cout << "foo(int &)" << std::endl; }

Is it possible to difference what function to call doing any modification in the calling arguments?

If the function foo is called in some of these ways:

foo( 10);

i = 10;
foo( static_cast<const int>(i));

foo( static_cast<const int&>(i)); 

it's called the first foo overloaded function, because it can't pass by reference a const argument to a non-const parameter. But, how would you do to call the second foo overload function? If I call the next way:

int i = 10;
foo( i);

It happens an ambiguous error because both functions are valid for this argument.

In this link https://stackoverflow.com/a/5465379/6717386 it's explained one way to resolve it: using objects instead of built-in types and doing private the copy constructor, so it can't do a copy of object value and it has to be called the second foo overload function and passing the object by reference. But, is there any way with the built-in types? I have to change the name of function to avoid the overloading?

Community
  • 1
  • 1
Carlos A. Gómez
  • 362
  • 4
  • 12

6 Answers6

18

You may do a cast (of the function) to select the overload function:

static_cast<void (&)(int&)>(foo)(i);

Demo

sergej
  • 17,147
  • 6
  • 52
  • 89
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks, it works fine!. But, I'm trying your solution with classes and methods and it appears the error _error: invalid static_cast from type '' to type 'void (&)(Punto&)'_. It's [here link:](http://ideone.com/ZxrqOt) – Carlos A. Gómez Aug 15 '16 at 16:55
  • 1
    @CarlosUrda You need to include the class name in the method pointer, e.g. static_cast(&MyClass::foo); – Resurrection Aug 15 '16 at 16:59
  • 1
    @CarlosUrda: Syntax is even uglier, see [Demo](http://coliru.stacked-crooked.com/a/e83fc0f4e3e76603). – Jarod42 Aug 15 '16 at 17:02
  • 1
    @CarlosUrda: `const` is missing in the cast for the method : [Demo](http://ideone.com/LFGS1W) – Jarod42 Aug 15 '16 at 21:56
4

In most instance, function overloading involves distinct parameter types and different input parameter lengths.

Your attempt is generally a bad practice and the resulting compiled code is compiler dependent and code optimization may even worsen things even more.

You may consider simply adding a second parameter to the second method, something like this:

void foo(int x)  { std::cout << "foo(int)"   << std::endl; }
void foo(int& x, ...) { std::cout << "foo(int &, ...)" << std::endl; }

where ... could be a boolean type, say: bool anotherFunction

So calling foo(param1, param2) would simply call the second code and everybody is fine.

skypjack
  • 49,335
  • 19
  • 95
  • 187
Gamma.X
  • 554
  • 4
  • 9
  • 1
    Compiler dependent ? Unless your compiler is broken, this has no reason to change behaviour whatsoever. – Quentin Aug 15 '16 at 15:54
  • 1
    Thanks, but I don't like your solution. I'm trying use the same function name for setter a getters in a class. `class Circulo { public: void centro( const Punto&); // setter void centro( Punto&) const; // getter private: Punto centro_; };` – Carlos A. Gómez Aug 15 '16 at 16:27
  • 1
    Sorry, but the question wasn't explicit enough. I just think keeping simple things simple is the key. However, about the getter and setter, for a more readable code, you can consider NOT using parameters for your getters. You would've still prevented this error if you follow this approach and your code would still be very clean. – Gamma.X Aug 15 '16 at 17:24
  • -Jarod42 had a point, but do you have to static('ally) cast your getter / setter to use them? lets keep it simple please. @Quentin ever wondered why your c++ compiler converts your old Uniform initializers into functions? See this: https://herbsutter.com/elements-of-modern-c-style/ Skip to Uniform initializers. – Gamma.X Aug 15 '16 at 17:42
  • 1
    @CarlosUrda: then simply do `class Circle { const Point& Center(); const;Point& Center(); /* ... */}; – Jarod42 Aug 15 '16 at 17:47
  • @Gamma.X most vexing parse...? I don't really get your point. Maybe I simply misunderstood your phrase, but if some code's behaviour depends on the compiler, or even on the optimizer, then it is at least implementation-defined (and undefined at worst). There is no such thing in this question. – Quentin Aug 15 '16 at 18:04
  • @Jarod42 You're absolutely right. Getter method exists mainly for getting the value, not for setting in a variable the value to get. ¡Mil gracias! – Carlos A. Gómez Aug 15 '16 at 18:31
  • 1
    @CarlosUrda - My point is that both functions are syntactically identical when you consider supplying a NULL in the function. Hence, the ambiguity. However, like I said, if you've NOT attempted using a parameter for your getter method. We probably could be discussing something else right now :) – Gamma.X Aug 15 '16 at 18:47
4

Very strange design, but if you want... I'll offer a solution as strange as your design Use Xreference in function signature. Then in the function you can check what you need to do using std::is_lvalue_reference, std::is_rvalue_reference.
Something like this

template<class T>
void foo(T&& x)
{
  static_assert(std::is_same<std::decay_t<T>, int>::value, "!"); 
  if (std::is_rvalue_reference<T&&>::value)
    std::cout << "do here what you want in foo(int x)";
  else
    std::cout << "do here what you want in foo(int & x)";
}

int main()
{
  int x = 5;
  foo(x); //"do here what you want in foo(int x)" - will be printed
  foo(std::move(x)); //"do here what you want in foo(int & x)" - will be printed
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
Andrey Nekrasov
  • 456
  • 2
  • 12
2

Despite the good answer of @Jarod42, as an alternative solution you can rely on a templated entry point and the overloading of an internal function (if you don't want to deal with explicit casts, of course).
It follows a minimal, working example:

#include<type_traits>
#include<iostream>
#include<utility>

void foo_i(char, int x)  { std::cout << "foo(int)"   << std::endl; }
void foo_i(int, int &x) { std::cout << "foo(int &)" << std::endl; }

template<typename T>
void foo(T &&t) {
    static_assert(std::is_same<std::decay_t<T>, int>::value, "!");
    foo_i(0, std::forward<T>(t));
}

int main() {
    foo( 10);
    int i = 10;
    foo( static_cast<const int>(i));
    foo( static_cast<const int &>(i)); 
    foo(i);
}

The static_assert serves the purpose of checking the parameter to be something that involves int (that is int, int &, const int &, int &&`, and so on).

As you can see from the code above, foo(i) will print:

foo(int &)

As expected.

skypjack
  • 49,335
  • 19
  • 95
  • 187
1

Another one:

#include <iostream>
#include <functional>

void foo(int x)
{
    std::cout << "foo(int)\n";
}

template<typename T>
void foo(T&& x)
{
    std::cout << "foo(int&)\n";
}

int main()
{
    int i = 10;
    foo(i);           // foo(int)
    foo(std::ref(i)); // foo(int&)
}
Loreto
  • 674
  • 6
  • 20
  • bad solution. In general it is a bad idea to overload functions the only argument of which is Xreference. For example: const int i = 10; foo(i); will print "foo(int&)\n" – Andrey Nekrasov Aug 16 '16 at 14:04
  • @AndreyNekrasov Well, another bad solution, perhaps, but `const int i = 10; foo(i);` prints foo(int) in my gcc. – Loreto Aug 16 '16 at 14:31
-1

I just happened to have stumbled upon this post and was surprised not to find the typical SFINAE solution. So, there you go:

#include <iostream>
#include <type_traits>

template<typename T,
    typename std::enable_if<!std::is_lvalue_reference<T>{}, int>::type = 0>
void foo(T)
{ std::cout << "foo(int)"   << std::endl; }

template<typename T,
    typename std::enable_if<std::is_lvalue_reference<T>{}, int>::type = 0>
void foo(T&)
{ std::cout << "foo(int &)" << std::endl; }
 
int main() {
    int i = 42;
    int& r = i;
    foo<decltype(i)>(i);
    foo<decltype(r)>(r);
}

Live example

303
  • 2,417
  • 1
  • 11
  • 25
  • The only way to call this is `foo` since `T` is not used in the implementation; why do you feel SFINAE is appropriate in this case? Your solution is not an _overload_ since it requires explicit instantiation to name the function – Human-Compiler Dec 14 '22 at 02:52
  • @Human-Compiler I was just wondering if constraining the functions could make this work somehow. But you're right, there is no overload resolution taking place here, so I guess the SFINAE reference is misplaced. We could still induce overload resolution of function templates by constraining both functions, which would look a bit nicer with C++20. – 303 Dec 14 '22 at 15:15
  • @Human-Compiler Could you reconsider your comment, since your it no longer applies anymore? – 303 Dec 21 '22 at 12:30