为什么命名变量调用被解析为T&&而不是const T&?

时间:2014-03-23 00:09:55

标签: c++ c++11

正如标题所说,为什么命名变量调用被解析为T&&而不是const T&函数?

#include <iostream>

template<typename T>
void f(T&& v)
{
    std::cout << "void f(T&& v)" << std::endl;
}

template<typename T>
void f(const T& v)
{
    std::cout << "void f(const T& v)" << std::endl;
}

int main()
{
    int i = 0;

    f(1);
    f(i);
}

在这种情况下,即使f()已命名,两个调用都会解析为i的第一个版本。一种解决方案是添加:

template<typename T>
void f(T& v)
{
    std::cout << "void f(T& v)" << std::endl;
}

或将第一个版本更改为:

template<typename T>
typename std::enable_if<!std::is_reference<T>::value, void>::type f(T&& v)
{
    std::cout << "void f(T&& v)" << std::endl;
}

但我想了解这一决定背后的原因。

2 个答案:

答案 0 :(得分:14)

扣除额为T = int &,表示f(T &&) == f(int &)。重载决策规则([over.ics.rank / 13.3.3.2])表示这是一个比f(int const &)更严格的匹配。两者都被归类为“完全匹配”(将值绑定到引用),但首选CV参数较少的参考。

答案 1 :(得分:1)

一个较少受到标准限制的答案是在声明中承认:

template<typename T>
void f(T&& v)

因为T是推导类型,T&& v是Scott Meyers所谓的universal reference,它可以绑定到rvalues和lvalues。 T可以推断为引用类型,因此您实际上正在调用f<int&>(int& && v),此时引用折叠规则生效,使此函数f<int&>(int& v)的名义签名成为,正如前面的答案所指出的,它是非const int参数的更好匹配。

在迈耶斯&#39;即将到来的Effective Modern C++他有以下项目:

  • 第30项:通过std :: move传递和返回rvalue引用,通过std :: forward进行通用引用。
  • 第31项:避免在通用引用上超载。

第31项告诉你不该做什么。第30项建议你可以做什么,只写一个f的重载,并用v完全转发std::forward

template<typename T>
void f(T&& v)
{
    std::cout << "void f(T&& v)" << std::endl;

    if (std::is_rvalue_reference<decltype(v)>::value)
        std::cout << "rvalue" << std::endl;
    else
        std::cout << "lvalue" << std::endl;

    std::cout << "v = " << std::forward<T>(v) << std::endl;
}