【问题标题】:Rvalue Reference in Type Deduction in C++11 [duplicate]C ++ 11中类型推导中的右值引用[重复]
【发布时间】:2014-12-27 17:17:10
【问题描述】:

在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 类型。

只是要记住的东西吗?还是?

【问题讨论】:

  • 请注意,当X 是引用类型时,您提供的示例将变得不正确,例如double&amp;。在这种情况下,X 仍会被推导出为double,但参数类型将是double&amp;,因此它们是不同的。没有单一类型X,而是两种类型,推导类型和参数类型。

标签: c++ c++11


【解决方案1】:

唯一要回答的问题是为什么T&amp; 导致T&amp; 而不是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&amp;&amp;... 的类型被推导出为double &amp;。这样,Manager 类构造函数的行为就像

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

std::forward&lt;double &amp;&gt;(d) 的效果基本上是一个static_cast&lt;double&amp;&amp;&gt;((double &amp;) d),根据引用折叠规则,它仍然是(double &amp;) (d)。结果,构造函数变成了

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

这样,变量被正确地传递给Widget类,并且还确保了正确的构造函数——接受引用的构造函数——被调用。


相反,如果类型不是推导出为double &amp;,而是double,则Manager 类构造函数的行为类似于

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

翻译成

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

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

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

【讨论】:

  • 感谢您的回复。我试图理解为什么传入左值意味着 T 被推断为 T& 类型,而传入右值意味着 T 被推断为 T 类型。这只是记忆的规则吗?
  • 我添加了更多信息。希望读者明白我的意思……可能只是一个愚蠢的问题,它只是一个要记住的规则。
  • 我写了一些更多的细节,并更正了示例的基本原理。
  • 感谢您的帖子。我已经阅读了几篇关于右值引用和相关的文章。你的帖子帮助我理解了一些缺失的部分——当折叠规则开始时。我不知道它们在调用 std::forward() 时开始。谢谢!
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多