Mircea Ispas
Mircea Ispas

Reputation: 20780

C++ template arguments deduction for rvalues refs vs const lvalue refs

I have next code:

#include <utility>

template<typename T>
struct fun
{
    fun(T&&){}
    fun(const T&){}
};

template<typename T>
fun<T> create_fun(T&& val)
{
    return fun<T>(std::forward<T>(val));
}

template<typename T>
fun<T> create_fun(const T& val)
{
    return fun<T>(val);
}

int main()
{
    int i = 1;
    const int ci = 2;

    fun<int> a = create_fun<int>(1);
    fun<int> b = create_fun<int>(i);
    fun<int> c = create_fun<int>(ci);

    fun<int> d = create_fun(1);
    fun<int> e = create_fun(i);
    //error: no viable conversion from fun<int&> to fun<int>
    //why is create_fun(T&& val) a better match than create_fun(const T& val)?
    fun<int> f = create_fun(ci);

    return 0;
}

Can you please explain me why for non const lvalues the universal reference is a better match?

How may I achieve the same behaviour as the one from a, b and c without explicitly specifying the type to create_fun?

EDIT:

I want to force fun to be generated with int as parameter for all 3 calls

#include <utility>

template<typename T>
struct fun
{
    fun(T&&){}
};

template<typename T>
fun<T> create_fun(T&& val)
{
    return fun<T>(std::forward<T>(val));
}

template<typename T>
struct show_type;

int main()
{
    int i = 1;
    const int ci = 2;

    show_type<decltype(create_fun(1))>();
    show_type<decltype(create_fun(i))>();
    show_type<decltype(create_fun(ci))>();

    return 0;
}

Upvotes: 2

Views: 231

Answers (1)

T.C.
T.C.

Reputation: 137310

Given create_fun(i); with a non-const lvalue i, template argument deduction for the two competing overloads results in:

fun<T> create_fun(T&& val) ==> fun<int&> create_fun(int& val)  (T = int &)
fun<T> create_fun(const T& val) ==> fun<int> create_fun(const int& val)  (T = int)

The first one is a better match than the second by §13.3.3.2 [over.ics.rank]/3.1.6. So the first one is selected.

Instead of using two overloads, you can just use a single universal reference template, and remove reference and const from T in create_fun's return type:

template<typename T>
using remove_cv_and_ref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

template<typename T>
fun<remove_cv_and_ref_t<T>> create_fun(T&& val)
{
    return fun<remove_cv_and_ref_t<T>>(std::forward<T>(val));
}

Demo.

Upvotes: 3

Related Questions