用右值/左值理解模板参数推导

Posted

技术标签:

【中文标题】用右值/左值理解模板参数推导【英文标题】:Understanding template argument deduction with rvalue/lvalue 【发布时间】:2014-03-04 09:03:18 【问题描述】:

这是function template does not recognize lvalue的后续报道

让我们玩一下下面的代码:

#include <iostream>
template <class T>
void func(T&&) 
  std::cout<<"in rvalue\n";


template <class T>
void func(const T&) 
  std::cout<<"in lvalue\n";


int main()

    double n=3;
    func<double>(n);
    func(n);

打印出来:

in lvalue
in rvalue

我不明白第二次通话中发生了什么。编译器如何 解析模板参数?为什么没有歧义?

【问题讨论】:

您不妨参考标准中的14.8.2。模板参数推导是一个有点复杂的主题。关键是选择了“最佳匹配”,并且T 可以推断为引用类型。 “有点复杂的主题”!我完全同意。顺便问一下,有没有办法让 g++/clang++ 详细说明它在这里做什么? 那个无辜的小部分占据了 15 页,并开发了一个全新的数学符号:-S 但最终它“按照你的想法做”,你只需要在 a 的上下文中接受模板参数作为诚实的类型函数签名。 我发现这个链接很有帮助:isocpp.org/blog/2012/11/… 不,它不打印日志,或者它的作用。但是模板警告/错误非常冗长,如果这就是您的意思的话。 【参考方案1】:

当您说func&lt;double&gt;(n) 时,没有参数推导,因为您指定了参数,因此可以在func(double &amp;&amp;)func(const double &amp;) 之间进行选择。前者不可行,因为右值引用不能绑定到左值(即n)。

只有func(n) 执行参数推导。这是一个复杂的话题,但简而言之,您有以下两个可能的候选人:

T = double &:    func(T &&)       -->   func(double &)          (first overload)
T = double:      func(const T &)  -->   func(const double &)    (second overload)

第一个重载严格来说更好,因为它需要更少的参数值转换(即从doubleconst double)。

神奇的成分是"reference collapsing",这意味着当T本身是一个引用类型时T &amp;&amp;可以是一个左值引用(具体来说,double &amp; &amp;&amp;变成double &amp;,这允许第一个推导存在)。

【讨论】:

我认为 Herb Sutter 的文章 Why Not Specialize Function Templates? 也有助于理解这种行为。 @piwi 除了自从引入右值引用以来它已经过时了。 @Kerrek 并没有说得足够清楚:当模板参数推导发生时,func( T &amp;&amp; 中的 &amp;&amp; ) 不是 右值引用,而是一个通用引用,它可以成为右值引用(如果参数是右值)或左值引用(如果参数是左值)。 Scott Meyer 在isocpp.org/blog/2012/11/… 中很好地解释了这一点;这是我针对这个特定问题推荐的文章。 @JamesKanze 我知道&amp;&amp; 的双重含义,具体取决于上下文,但您是对的,Herb Sutter 的文章没有涉及这一点;但是,我发现在“混合”模板和重载时理解解析规则很有用。感谢您的链接! @piwi 这绝对是一篇有用的文章,而且写得很好。它只是没有解决这里有问题的行为(主要是因为在撰写文章时该行为不存在)。 @JamesKanze:我链接到另一个答案,该答案已经说明了所有细节,我不想在这里重复......

以上是关于用右值/左值理解模板参数推导的主要内容,如果未能解决你的问题,请参考以下文章

模板中右值的类型推导

c++11:模板

重新理解C11的右值引用

c++11-17 模板核心知识—— 理解模板参数推导规则

左值和右值的区别

Effective Modern C++ 条款28 理解引用折叠