[英]How to have template type deduced in std::function arguments with lambda?
[英]How are std::move template parameters deduced?
假设我们有:
foo(A&& a);
如果你这样做
A a;
foo(a);
它不会编译,抱怨不能将左值绑定到A &&。 那很好。
但是,鉴于std :: move的签名,
template<class T> typename remove_reference<T>::type&& std::move(T&& a);
看起来它需要一个右值引用,就像在foo中一样,为什么下面的代码符合?
A a;
std::move(a);
没有a
是左值?
furthur,据说编译将实例化:
typename remove_reference<A&>::type&& std::move(A& && a);
我不明白为什么不是:
typename remove_reference<A>::type&& std::move(A && a);
在我看来a
类型为A
,而不是A&
Nope move
没有采用rvalue-reference,它采用了被社区称为通用引用的内容。 按类型推导的模板参数根据参考折叠规则进行操作。 这意味着:
T
是K
,那么T&&
将只是K&&
; T
是K&
,则T&&
将崩溃为K&
; T
是K&&
,则T&&
将崩溃为T&&
。 它就像&
和&&
的逻辑 - 和&
,其中&
是0, &&
是1:
& &&
|-----------|
& | & | & |
|-----|-----|
&& | & | && |
|-----------|
这就是rvalues和lvalues的move
方式。
例子:
template<typename T>
void f(T&&);
f<int> // T is int; plugging int into T makes int&& which is just int&&
f<int&> // T is int&; plugging int& into T is int& && which collapse to int&
f<int&&> // T is int&&; plugging int&& into T is int&& && which collapse to int&&
请注意,引用折叠仅发生在模板参数中; 你不能直接输入int&& &&
并期望它编译。 当然,您没有像这样手动指定类型。 这些仅仅是为了显示哪些引用崩溃了。
所以你真的这样称呼它:
int i;
f(i); // T is int&; int& && collapses to int&
f(4); // T is int&&; int&& && collapses to int&&
参考折叠也是move
不返回T&&
的原因:如果T
是左值引用,则引用将会崩溃,并且move
只返回左值引用。 你执行remove_reference
来获得非引用类型,以便&&
真正意味着“rvalue-reference”。
您可以在此处了解更多信息: http : //isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
句法形式T&&
式演绎的背景下 (包括模板参数推导 ,但例如也宣告作为变量的类型扣除auto
) 不表示右值引用,而是什么斯科特迈尔斯称之为[ 通用参考 ]。 请注意,只有非常特殊的句法形式 T&&
表示普遍参考,而其他类似形式则不被视为此类。 例如:
template<typename T>
void foo(T&& t); <-- T&& is a universal reference
template<typename T>
void foo(T const&& t); <-- T const&& is NOT a universal reference
template<typename T>
void foo(S<T>&& t); <-- S<T>&& is NOT a universal reference
template<typename T>
struct S { void foo(T&& t); }; <-- T&& is NOT a universal reference
通用引用可以绑定到左值和右值。 如果绑定了类型A
的左值,则推断T
为A&
并且由于参考规则折叠 ( A& &&
变为A&
),参数的类型将解析为A&
(左值引用)。 如果绑定了类型A
的右值,则推导出T
为A
并且参数的类型解析为A&&
(右值引用)。
[ 注意 : 参考折叠规则可能看起来很复杂,但它们实际上非常简单:引用Stephan T. Lavavej,“左值引用具有传染性”,这意味着当形式T&& &
, T& &
T& &&
或T& &&
实例化时,它们总是如此解析为T&
- 只有表格T&& &&
被解析为T&&
]
这就是当参数是左值 ( T
被推断为T&
)时, std::move
函数模板将如下实例化的原因:
typename remove_reference<A&>::type&& std::move(A& && a);
当参数是一个rvalue ( T
推断为A
)时,它将被实例化如下
typename remove_reference<A>::type&& std::move(A&& a);
尽管其他人已经说过,但标准只讨论了右值引用。
这对于std :: move的工作原理的关键是模板参数推导规则中的一个明确的特殊规则:
[...]如果[声明的函数参数类型]是对cv-unqualified模板参数的右值引用,并且参数是左值,则使用类型“对A的左值引用”代替A来进行类型推导。[ ...]
另一部分是参考折叠的规则,这就是说
如果类型template-parameter [...]表示类型TR是对类型T的引用,则尝试创建类型“对cv TR的左值引用”会创建类型“对T的左值引用” “,尝试创建类型”对cv TR的rvalue引用“会创建类型TR。
现在在template<class T> typename remove_reference<T>::type&& std::move(T&& a);
函数参数a匹配上面的规则(“rvalue reference to cv-unqualified template parameter”),因此推导出的类型将是参数类型的左值引用(如果参数是左值)。 在你的情况下,导致T = A&。
将其替换为移动收益率的声明
remove_reference<A&>::type&& std::move<A&>(A& && a);
使用remove_reference的定义和参考折叠规则(对TR => TR的rvalue引用),使得:
A&& std::move<A&>(A& a);
Scott Meyer的通用参考概念,在其他答案中提出,是一种有用的方法来记住类型推导和参考折叠规则组合的这种令人惊讶的效果:对推导类型的rvalue引用可能最终成为左值引用(如果该类型可以推导为左值参考)。 但是标准中没有通用的引用 。 斯科特迈尔斯说:这是谎言 - 但谎言比真相更有帮助......
请注意,std :: forward在此主题上有一个不同的转折:它使用额外的间接来防止参数推断(因此必须明确给出类型),但也使用引用折叠将左值作为左值并将右值作为右值转发。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.