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?

6
  • 1
    Did you try foo( static_cast<int&>(i));? Commented Aug 15, 2016 at 15:02
  • 1
    You could use void foo(int x) and void foo(int *x) instead. Commented Aug 15, 2016 at 15:04
  • @mvidelgauz Yes, I have tried it. Same error than above. :( Commented Aug 15, 2016 at 15:05
  • @DimChtz Yes, that way works. But using references? Commented Aug 15, 2016 at 15:07
  • 7
    The easiest solution would be to write better functions. Or put them in different namespaces. Commented Aug 15, 2016 at 15:07

6 Answers 6

18

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

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

Demo

Sign up to request clarification or add additional context in comments.

4 Comments

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 '<unresolved overloaded function type>' to type 'void (&)(Punto&)'. It's here link:
@CarlosUrda You need to include the class name in the method pointer, e.g. static_cast<void (MyClass::*)(int&)>(&MyClass::foo);
@CarlosUrda: Syntax is even uglier, see Demo.
@CarlosUrda: const is missing in the cast for the method : Demo
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.

8 Comments

Compiler dependent ? Unless your compiler is broken, this has no reason to change behaviour whatsoever.
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_; };
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.
@CarlosUrda: then simply do `class Circle { const Point& Center(); const;Point& Center(); /* ... */};
@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 :)
|
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
}

Comments

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.

Comments

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&)
}

2 Comments

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"
@AndreyNekrasov Well, another bad solution, perhaps, but const int i = 10; foo(i); prints foo(int) in my gcc.
-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

3 Comments

The only way to call this is foo<decltype(r)> 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 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.
@Human-Compiler Could you reconsider your comment, since your it no longer applies anymore?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.