Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Distinguish between function references/pointers accepting const and non-const argument with same name as function parameter

Let's consider two functions with same names:

int func(int&)
{
    return 7;
}
int func(const int&)
{
    return 5;
}

Let int mutableValue = 5 be defined somewhere. Is there any possibility that template function call in call(mutableValue, func) would take only int func(int&)? I don't want to use static_cast<int(&)(int&)> - because it's so noisy.

Naive implementation:

template<typename Arg, typename Ret>
void call(Arg& val, Ret (&fun)(Arg&))
{
    fun(val);
}

works on gcc, but does not work on clang - solutions must work on both compilers.

Accepting only const Arg& is easy. Deleting const Arg& overload does not help.

like image 270
MamCieNaHita Avatar asked Jan 24 '20 09:01

MamCieNaHita


People also ask

What is the difference between a reference parameter and a const reference parameter?

The important difference is that when passing by const reference, no new object is created. In the function body, the parameter is effectively an alias for the object passed in. Because the reference is a const reference the function body cannot directly change the value of that object.

What is the difference between const reference and reference?

A const reference is actually a reference to const. A reference is inherently const, so when we say const reference, it is not a reference that can not be changed, rather it's a reference to const. Once a reference is bound to refer to an object, it can not be bound to refer to another object.

Why can't you assign a const reference to a non const reference?

Once you have a const object, it cannot be assigned to a non-const reference or use functions that are known to be capable of changing the state of the object. This is necessary to enforce the const-ness of the object, but it means you need a way to state that a function should not make changes to an object.

What is a non const reference C++?

I understand now that a const reference to a non const object does have a purpose: to prevent the reference from modifying the object. The non const object can still be modified by other means but not by this const reference.


2 Answers

I believe clang is the only compiler that gets this right. This should not compile. As long as the argument to fun is an overload set that does not contain any function templates, template argument deduction will be attempted for each member of the overload set. If template argument deduction would succeed for more than one of these overloads, the function parameter becomes a non-deduced context [temp.deduct.call]/6.2.

In the example in question, the argument to fun is the overload set func, which does indeed not contain any function templates. Thus, argument deduction on fun is attempted for both overloads of func, which succeeds. As a result, the parameter fun becomes a non-deduced context, which means that no argument can be deduced for Ret and the call fails as there are no candidates (exactly what clang complains about).

To disambiguate this call, simply explicitly specify the argument for the first template parameter:

call<int>(mutableValue, func)
like image 164
Michael Kenzel Avatar answered Dec 08 '22 01:12

Michael Kenzel


Since it seems to be impossible to resolve the ambiguity in one template argument deduction:

If you are ok with a change of the syntax at the call site you can separate the call into two calls/deduction passes:

template<typename Arg>
auto call(Arg& val)
{
    return [&](auto (&fun)(Arg&)){ fun(val); };
}

to be called as

call(mutableValue)(func)

Another downside is however that the lambda could be stored by a caller and accidentally used later when the captured reference isn't valid anymore.

You could maybe hide this in a macro call, so that the syntax matches what you want and to reduce the potential for misuse.

like image 35
walnut Avatar answered Dec 08 '22 01:12

walnut