简体   繁体   中英

Type not deduced to be r-value reference: why not?

Please consider the following code:

class CMyClass {};

template<class T>
void func(T&& param) {
        if (std::is_same<CMyClass, std::decay<T>::type>::value)
            std::cout << "param is a CMyClass\n";
        if (std::is_same<T, CMyClass&>::value)
            std::cout << "param is a CMyClass reference\n";
        else if (std::is_same<T, CMyClass&&>::value)
            std::cout << "param is a CMyClass r-value reference\n";
        else if (std::is_same<T, const CMyClass&>::value)
            std::cout << "param is a const CMyClass reference\n";
        else if (std::is_same<T, const CMyClass&&>::value)
            std::cout << "param is a const CMyClass r-value reference\n";
        else if (std::is_same<T, const CMyClass>::value)
            std::cout << "param is a constant CMyClass\n";
        else if (std::is_same<T, CMyClass>::value)
            std::cout << "param is a CMyClass\n";
        else
            std::cout << "param is not a CMyClass\n";
}


CMyClass mc3;
func(std::move(mc3));

The output from this little program is

param is a CMyClass
param is a CMyClass

Why has the type of mc3 not been deduced to be an r-value reference please?

I can't find a good dupe for this even though one must exist somewhere, sorry.

The deduction rules for:

template <class T>
void foo(T&& )

in the context of a call foo(expr) are:

  • If expr is an lvalue of type U , then T is deduced as U& and the type T&& is U& , due to reference collapsing.
  • If expr is an rvalue of type U , then T is deduced as U the type T&& is U&& , due to reference collapsing.

In your example, std::move(mc3) is an rvalue (specifically an xvalue) of type CMyClass . Hence, T is deduced as CMyClass . This check:

else if (std::is_same<T, CMyClass&&>::value)
    std::cout << "param is a CMyClass r-value reference\n";

will almost never be true as T will never deduce as an rvalue reference type. It could be specifically provided as such:

func<CMyClass&&>(std::move(mc3));

but that's an unlikely usage. What you can do instead is check:

else if (std::is_same<T&&, CMyClass&&>::value)
//                    ~~~~

That will handle all cases where the argument is an rvalue. Indeed, if you simply always check for T&& , that will handle all of your cases properly.

Why has the type of mc3 not been deduced to be an r-value reference please?

If param is an rvalue , T is a non-reference inside func , and T&& is an rvalue reference . Here's an example that empirically shows what T means in the body of a function that takes a forwarding reference :

template <typename T>
void func(T&& x)
{
    std::is_same<T, something>{}; // (0)
    std::is_same<T&&, something>{}; // (1)
}

In the case of (0) :

  • T is T when an rvalue is passed to func . (*)
  • T is T& for when an lvalue is passed to func .

In the case of (1) :

  • T&& is T&& when an rvalue is passed to func .
  • T&& is T& when an lvalue is passed to func .

If you use std::is_same<T&&, CMyClass>::value , you should either get a T& or T&& .


(*) : note that the term "is" is inaccurate - the various meanings of T inside func depend on template argument deduction and reference collapsing .

In short:

  • T is deduced as:

    • T& if x is an lvalue .

    • T otherwise.

  • Due to reference collapsing , T&& is:

    • T& if x is an lvalue . ( T& && -> T& )

    • T&& otherwise. ( T&& && -> T&& )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM