简体   繁体   English

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

[英]Preventing non-const lvalues from resolving to rvalue reference instead of const lvalue reference

I'm having trouble overloading a function to take a value either by const reference or, if it is an rvalue, an rvalue reference. 我无法通过const引用重载函数来获取值,或者如果它是rvalue则是rvalue引用。 The problem is that my non-const lvalues are binding to the rvalue version of the function. 问题是我的非const左值绑定到函数的右值版本。 I'm doing this in VC2010. 我在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&&)
}

The priority seems to be to deduce foo(x) as 优先级似乎是推导foo(x)为

foo< vector<int> & >(vector<int>& && t)

instead of 代替

foo< vector<int> >(const vector<int>& t)

I tried replacing the rvalue-reference version with 我尝试用r替换rvalue-reference版本

void foo(typename remove_reference<T>::type&& t)

but this only had the effect of causing everything to resolve to the const-lvalue reference version. 但这只会导致所有内容都解析为const-lvalue引用版本。

How do I prevent this behaviour? 我该如何防止这种行为? And why is this the default anyway - it seems so dangerous given that rvalue-references are allowed to be modified, this leaves me with an unexpectedly modified local variable. 为什么这仍然是默认值 - 考虑到允许修改rvalue-references似乎很危险,这给我留下了意外修改的局部变量。

EDIT: Just added non-template versions of the functions, and they work as expected. 编辑:刚添加了非模板版本的函数,它们按预期工作。 Making the function a template changes the overload resolution rules? 将函数设为模板会更改重载决策规则吗? That is .. really frustrating! 那真是令人沮丧!

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>&&)

When you have a templated function like this you almost never want to overload. 当你有一个模板函数这样你几乎从来没有要过载。 The T&& parameter is a catch anything parameter. T&&参数是一个catch任何参数。 And you can use it to get any behavior you want out of one overload. 并且您可以使用它从一个重载中获取您想要的任何行为。

#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>&
}

In order for T&& to bind to an lvalue reference, T must itself be an lvalue reference type. 为了使T&&绑定到左值引用, T必须本身是左值引用类型。 You can prohibit the template from being instantiated with a reference type T : 您可以禁止使用引用类型T实例化模板:

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

enable_if is found in <utility> ; enable_if<utility>找到; is_reference is found in <type_traits> . is_reference可在<type_traits>找到。

The reason that the overload taking T&& is preferred over the overload taking a T const& is that T&& is an exact match (with T = vector<int>& ) but T const& requires a qualification conversion (const-qualification must be added). 过载T&&的优先选择优先于过载采用T const&并且T&&是完全匹配( T = vector<int>& )但是T const&需要资格转换(必须添加const限定)。

This only happens with templates. 这只发生在模板上。 If you have a nontemplate function that takes a std::vector<int>&& , you will only be able to call that function with an rvalue argument. 如果你有一个带有std::vector<int>&&的nontemplate函数,你只能使用rvalue参数调用该函数。 When you have a template that takes a T&& , you should not think of it as "an rvalue reference parameter;" 当你有一个采用T&&的模板时,你不应该把它想象成“一个右值参考参数;” it is a "universal reference parameter" (Scott Meyers used similar language, I believe). 它是一个“通用参考参数”(Scott Meyers使用类似语言,我相信)。 It can accept anything. 它可以接受任何东西。

Allowing a T&& parameter of a function template to bind to any category of argument is what enables perfect forwarding. 允许函数模板的T&&参数绑定到任何类别的参数是实现完美转发的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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