2014-10-31 64 views
1

之前,模板类型推演很简单:右值引用的类型推演在C++ 11 C++ 11

template <typename X> 
void bar(X i) { 
    std::cout << __PRETTY_FUNCTION__ << std::endl; 
} 

X型将是调用者传递参数的任何类型

现在,对于C++ 11,我阅读了一篇关于Scott Meyers右值引用的文章。

它指出

template<typename T> 
void f(T&& param); 

在扣除类型为模板参数是一个普遍的参考,左值和相同类型的右值推导出有略为不同的类型。具体地,类型T的左值被推断为类型T &(即,左值参照T),而T型的右值被推断为是简单的类型T.

不知为什么类型的左值T被推断为T &类型,而T类型的右值被推断为简单的T类型。

这只是记住的东西吗?要么?

+0

请注意,当“X”是引用类型时,您呈现的示例会变得不正确。 '双&'。在这种情况下,'X'仍然会被推断为'double',但参数类型是'double&',因此它们是不同的。没有单一类型'X',而是两种类型,即推导类型和参数类型。 – davidhigh 2014-10-31 13:18:03

回答

4

唯一要回答的问题是为什么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) ...) {} 
}; 

如果调用Manager通过

double d=1.0; 
Manager(d); 

Args&&...类型被推断double &,根据规则,你我ntioned。由此,Manager类的构造表现为

Manager(double &d) : Widget(std::forward<double&>(d)) {} 

std::forward<double &>(d)效果就基本上一个static_cast<double&&>((double &) d),其通过引用折叠的规则仍然(double &) (d)。因此,构造变得

Manager(double &d) : Widget((double &) d) {} 

这样,变量正确传递给类Widget,并且它也确保了正确的构造 - 一个将参考 - 被调用。


相反,如果该类型没有推导出double &,而是作为double,则Manager类的构造函数会表现得像

Manager(double &d) : Base(std::forward<double>(d)) {} 

成为翻译成

Manager(double &d) : Base((double &&) (d)) {} 

也就是说,左值引用被转换为右值引用( - 好像已经应用了std::move)。

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

+0

感谢您的回复。我试图理解为什么传入一个左值意味着T被推断为T类型,而传入右值则意味着T被推断为T类型。是否只是记忆的规则? – Hei 2014-10-31 11:52:14

+0

我添加了更多信息。希望读者明白我的观点......可能只是一个愚蠢的问题,它只是记忆的一个规则。 – Hei 2014-10-31 11:57:29

+0

我写了一些更多的细节,也纠正了这个例子的基本原理。 – davidhigh 2014-10-31 12:58:11

0

我认为原因是T和T &表示作为参数类型出现时的左值;只是T表示左值作为局部变量,而T &表示“别处的左值”。

这是不太可能,实例化两个T,T &和T & &时,因为后者的语义与那些左值完全不同的通用功能,将工作做好。所以,你想要一个右值,你应该明确地声明它。