C++11中类型演绎中的右值引用

C++11中类型演绎中的右值引用,c++,c++11,C++,C++11,在C++11之前,模板类型推导非常简单: template <typename X> void bar(X i) { std::cout << __PRETTY_FUNCTION__ << std::endl; } 类型X将是调用者传入的任何类型的参数 现在,对于C++11,我读了一篇关于R值引用的文章 它说 template<typename T> void f(T&& param); 在对作为通用引用的模板参数进行类

在C++11之前,模板类型推导非常简单:

template <typename X>
void bar(X i) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}
类型X将是调用者传入的任何类型的参数

现在,对于C++11,我读了一篇关于R值引用的文章

它说

template<typename T>
void f(T&& param);
在对作为通用引用的模板参数进行类型推导的过程中,相同类型的左值和右值被推断为具有稍微不同的类型。特别是,T型的左值被推导出为T型&即,T型的左值引用T,而T型的右值被推导出为T型

我想知道为什么T型的左值被推导出为T型&而T型的右值被推导出为T型


这只是要记住的东西吗?或者?

唯一要回答的问题是为什么T&导致T&而不是T,因为它在正常类型的演绎中会导致T。对于这一点,我想答案是完美的`

<>编辑:更详细地,考虑一个完全转发类构造函数的情况:

struct Widget    //... to stay in Scott Meyers terminology
{
     double &x;
     Widget(double &_x) : x(_x) {}

};

class Manager : public Widget
{
     template<typename ... Args>
     Manager(Args&& ... args) : Widget(std::forward<Args>(args) ...) {}
};
参数的类型&&。。。根据您提到的规则推导为double&。这样,Manager类构造函数的行为如下

Manager(double &d) : Widget(std::forward<double&>(d)) {}
这样,变量就可以正确地传递给类小部件,并且还可以确保调用了正确的构造函数(引用的构造函数)

相反,如果不是将类型推断为double&而是double,则管理器类构造函数的行为如下

Manager(double &d) : Base(std::forward<double>(d)) {}
也就是说,左值引用被强制转换为右值引用——就好像应用了std::move一样


这样,可能仍然可以获取引用,因为cast没有更改变量的地址。但是,现在您无法确定是否调用了Widget中的正确构造函数-还可能有另一个构造函数使用右值引用,然后会错误地调用右值引用。

唯一要回答的问题是为什么T&会导致T&而不是正常类型推导中的T。对于这一点,我想答案是完美的`

<>编辑:更详细地,考虑一个完全转发类构造函数的情况:

struct Widget    //... to stay in Scott Meyers terminology
{
     double &x;
     Widget(double &_x) : x(_x) {}

};

class Manager : public Widget
{
     template<typename ... Args>
     Manager(Args&& ... args) : Widget(std::forward<Args>(args) ...) {}
};
参数的类型&&。。。根据您提到的规则推导为double&。这样,Manager类构造函数的行为如下

Manager(double &d) : Widget(std::forward<double&>(d)) {}
这样,变量就可以正确地传递给类小部件,并且还可以确保调用了正确的构造函数(引用的构造函数)

相反,如果不是将类型推断为double&而是double,则管理器类构造函数的行为如下

Manager(double &d) : Base(std::forward<double>(d)) {}
也就是说,左值引用被强制转换为右值引用——就好像应用了std::move一样


这样,可能仍然可以获取引用,因为cast没有更改变量的地址。但是,现在您无法确定是否调用了Widget中的正确构造函数-还可能有另一个构造函数使用右值引用,这将被错误地调用。

我认为原因是T和T&在作为参数类型出现时都表示左值;只是T将左值表示为局部变量,而T&表示来自其他地方的左值的别名


当同时为T、T&和T&&实例化时,泛型函数不太可能很好地工作,因为后者的语义与左值的语义大不相同。因此,如果您想要一个右值,您应该明确地声明它。

我认为原因是T和T&在作为参数类型出现时都表示左值;只是T将左值表示为局部变量,而T&表示来自其他地方的左值的别名


当同时为T、T&和T&&实例化时,泛型函数不太可能很好地工作,因为后者的语义与左值的语义大不相同。因此,如果您想要一个右值,您应该明确说明。

谢谢您的回复。我试图理解为什么传入左值意味着T被推断为T类型&而传入右值意味着T被推断为T类型。这只是一个记忆的规则吗?我添加了更多信息。希望读者理解我的观点…可能只是一个愚蠢的问题,这只是一个需要记住的规则。我写了更多的细节,并纠正了示例的基本原理。谢谢你的帖子。我读过几篇关于右值参考和相关的文章。你的帖子帮助我理解了一些缺失的部分——当崩溃的规则生效时。我不知道他们在调用std::forward时开始工作。谢谢谢谢你的回复。我试图理解为什么传入左值意味着T被推断为T类型&而传入右值意味着T被推断为T类型。这只是一个记忆的规则吗?我添加了更多信息。希望读者理解我的观点…可能只是一个愚蠢的问题,这只是一个需要记住的规则。我写了更多的细节,并纠正了示例的基本原理。谢谢你的帖子。我读过几篇关于右值的文章
参考和相关。你的帖子帮助我理解了一些缺失的部分——当崩溃的规则生效时。我不知道他们在调用std::forward时开始工作。谢谢请注意,如果X是引用类型,例如double&,则您提供的示例将不正确。在这种情况下,X仍将被推断为double,但参数类型将是double&,因此它们有所不同。没有单一类型X,但有两种类型,即推断类型和参数类型。请注意,当X是引用类型时,您提供的示例将变得不正确,例如,double&。在这种情况下,X仍将被推断为double,但参数类型将是double&,因此它们有所不同。没有单一的X类型,而是两种类型,推导类型和参数类型。