防止非常量左值解析为右值引用而不是const左值引用

Ayj*_*jay 19 c++ templates metaprogramming rvalue-reference

我无法通过const引用重载函数来获取值,或者如果它是rvalue则是rvalue引用.问题是我的非const左值绑定到函数的右值版本.我在VC2010中这样做.

#include <iostream>
#include <vector>

using namespace std;

template <class T>
void foo(const T& t)
{cout << "void foo(const T&)" << endl;}

template <class T>
void foo(T&& t)
{cout << "void foo(T&&)" << endl;}

int main()
{
    vector<int> x;
    foo(x); // void foo(T&&) ?????
    foo(vector<int>()); // void foo(T&&)
}
Run Code Online (Sandbox Code Playgroud)

优先级似乎是推导foo(x)为

foo< vector<int> & >(vector<int>& && t)
Run Code Online (Sandbox Code Playgroud)

代替

foo< vector<int> >(const vector<int>& t)
Run Code Online (Sandbox Code Playgroud)

我尝试用r替换rvalue-reference版本

void foo(typename remove_reference<T>::type&& t)
Run Code Online (Sandbox Code Playgroud)

但这只会导致所有内容都解析为const-lvalue引用版本.

我该如何防止这种行为?为什么这仍然是默认值 - 考虑到允许修改rvalue-references似乎很危险,这给我留下了意外修改的局部变量.

编辑:刚添加了非模板版本的函数,它们按预期工作.将函数设为模板会更改重载决策规则吗?那真是令人沮丧!

void bar(const vector<int>& t)
{cout << "void bar(const vector<int>&)" << endl;}

void bar(vector<int>&& t)
{cout << "void bar(vector<int>&&)" << endl;}

bar(x); // void bar(const vector<int>&)
bar(vector<int>()); // void bar(vector<int>&&)
Run Code Online (Sandbox Code Playgroud)

How*_*ant 24

当你有一个模板函数这样你几乎从来没有要过载.该T&&参数是catch any参数.并且您可以使用它从一个重载中获取您想要的任何行为.

#include <iostream>
#include <vector>

using namespace std;

template <class T>
void display()
{
    typedef typename remove_reference<T>::type Tr;
    typedef typename remove_cv<Tr>::type Trcv;
    if (is_const<Tr>::value)
        cout << "const ";
    if (is_volatile<Tr>::value)
        cout << "volatile ";
    std::cout << typeid(Trcv).name();
    if (is_lvalue_reference<T>::value)
        std::cout << '&';
    else if (is_rvalue_reference<T>::value)
        std::cout << "&&";
    std::cout << '\n';
}

template <class T>
void foo(T&& t)
{
    display<T>();
}

int main()
{
    vector<int> x;
    vector<int> const cx;
    foo(x); // vector<int>&
    foo(vector<int>()); // vector<int>
    foo(cx);  // const vector<int>&
}
Run Code Online (Sandbox Code Playgroud)

  • 很公平.我和其他一些人(彼得和戴夫)试图通过引入右值参考来一举两得:移动语义和完美转发.在那个时候(2002年),我们认为对语言的最小改变很少或没有向后不兼容并且完成不止一个不可能的任务将是一件好事.但如果我们一直在设计一个干净的板岩,当然更清洁的解决方案似乎是合理的.当然,如果这种挫折感增加,那么应该将自己限制在C++ 98/03子集中,因此不会出现这个问题. (6认同)
  • 是的,你已经明白了.虽然我的说法有所不同:非模板函数中的`&&`表示"move-semantics",模板函数中的`&&`表示"完美转发" (3认同)

Jam*_*lis 12

为了T&&绑定到左值引用,T它本身必须是左值引用类型.您可以禁止使用引用类型实例化模板T:

template <typename T>
typename std::enable_if<!std::is_reference<T>::value>::type foo(T&& t)
{
    cout << "void foo(T&&)" << endl;
}
Run Code Online (Sandbox Code Playgroud)

enable_if发现于<utility>; is_reference发现于<type_traits>.

过载取代T&&优先于过载的原因是a T const&T&&完全匹配(有T = vector<int>&)但T const&需要资格转换(必须添加const限定).

这只发生在模板上.如果你有一个带有a的nontemplate函数std::vector<int>&&,你将只能用一个rvalue参数调用该函数.当你有一个采用a的模板时T&&,你不应该把它想象成"一个右值参考参数;" 它是一个"通用参考参数"(Scott Meyers使用类似语言,我相信).它可以接受任何东西.

允许T&&函数模板的参数绑定到任何类别的参数是实现完美转发的原因.