Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you (and should you) disambiguate a function call taking T and const reference to T?

If we have:

void foo(int) {}
void foo(const int&) {}

we cannot call foo like this:

foo(3);

because the call is ambiguous:

error: call of overloaded 'foo(int)' is ambiguous
40 |     foo(3);
   |          ^
note: candidate: 'void foo(int)'
36 | void foo(int) {}
   |      ^~~
note: candidate: 'void foo(const int&)'
37 | void foo(const int&) {}
   |      ^~~

What we can do is explicitly provide the correct overload, for example via a function pointer:

auto (*ptr)(const int&) -> void = foo;
ptr(3); // calls the "const int&" overload, obviously

However, that kind of defeats the purpose of convenient overloads. The question is - can I somehow disambiguate the call in a more... elegant? way? Are there ever cases where it would be desired to provide both overloads, for T and for const T&?

like image 737
Fureeish Avatar asked Aug 05 '20 20:08

Fureeish


People also ask

When should one use const reference arguments in function 1 point?

When writing a C++ function which has args that are being passed to it, from my understanding const should always be used if you can guarantuee that the object will not be changed or a const pointer if the pointer won't be changed.

Should I always use const reference?

Yes, you should use const whenever possible. It makes a contract that your code will not change something. Remember, a non-const variable can be passed in to a function that accepts a const parameter. You can always add const, but not take it away (not without a const cast which is a really bad idea).


Video Answer


2 Answers

You can exploit templates. Overload resolution favours a non-template function over a template one, so converting one of the overloads to a template is a sufficent disambiguation:

#include <iostream>

void foo(int n)
{
    std::cout << "By Value  " << n;
}

template<int N = 0>
void foo(const int& n)
{
    std::cout << "By Reference " << n;
}

int main() {
    foo(1);
    foo<>(1);
}

Granted, you need <> to call the template one, but this could have some uses. Ostensibly more elegant than a function pointer? But alas it's not really much better than renaming say foo<> to bar.

like image 170
Bathsheba Avatar answered Oct 21 '22 08:10

Bathsheba


It seems like you're asking whether we can force the overload resolution mechanism to select one signature over the other, rather than explicitly spelling out the signature that you want.

As far as I know, the only way to force overload resolution to pick the int overload over the const int& overload is to cast the argument to a volatile int glvalue, and there is no way to force overload resolution to pick the const int& overload over the int overload.

In any case, I can't think of any reason why one would want to write this particular set of overloads.

like image 31
Brian Bi Avatar answered Oct 21 '22 08:10

Brian Bi