我对此one有一个后续问题:
符号P
和A
指的是temp.deduct.call部分
如果我正确理解模板参数推导,以下代码将发生以下情况:
template<typename T>
void foo(const T& a);
int b;
foo(std::move(b));
P
和A
。我们推断出声明是引用const T&
(而不是转发引用)的情况A
:std::move(b)
的类型为int&&
[xvalue]->,其类型已调整为A:= int
([7.2.2#1])P
:const T&
->删除const和引用([12.9.2.1#3])-> P:= T
A
的模式匹配P
->结果T:= int
。两个问题:
std::move(b)
是一个表达式,我一直认为它的类型是int&&
(因为std::move
返回一个int&&
),但是([7.2.2#1])告诉了一些不同的东西,这意味着在进行任何分析之前就删除所有引用,因此当谈论表达式的类型时,永远不会涉及任何引用:struct A{ A& operator+(const A&);}
A a, b;
auto c = a + b;
因此a+b
很明显会返回A&
。但表达式的类型为A
。那是对的吗 ? declval(a+b)
是另一种野兽,并返回A&
。
答案 0 :(得分:4)
该引用已从表达式类型中删除。但是,表达式具有另一个属性 value-category ,该属性映射到函数调用表达式[expr.call]/14的引用类型:
如果结果类型是左值引用类型或对函数类型的右值引用,则函数调用为左值;如果结果类型是对对象类型的右值引用,则为xvalue;否则为prvalue。
这几乎可以由那些推理规则来表示:
function return type function call expression [type,value-category]
T& => [ T , lvalue ]
T&& => [ T , xvalue ]
T => [ T , prvalue ]
decltype
执行反向映射,[dcl.type.decltype]/1:
对于表达式e,由decltype(e)表示的类型定义如下:
- [...]
否则,如果e为x值,则decltype(e)为T &&,其中T为e的类型;
否则,如果e为左值,则decltype(e)为T&,其中T为e的类型;
否则,decltype(e)是e的类型。
因此,通过引用将其带入类型的信息不会因删除[expr.type]中的引用而丢失。此信息由值类别表示。