![](/img/trans.png)
[英]Does a Comparison Between an Lvalue and a Literal Invoke an Lvalue-to-Rvalue Conversion?
[英]Does lvalue-to-rvalue conversion is applied to non-type lvalue template argument?
struct S{
constexpr S() {};
};
template <auto x> void f();
int main() {
S s{};
f<s>();
}
首先,根据[temp.arg.nontype]/1
如果模板参数 (13.2) 的类型 T 包含占位符类型 (9.2.9.6) 或推导的 class 类型 (9.2.9.7) 的占位符,则参数的类型是为变量 x 推导的类型发明宣言
T x = template-argument;
如果模板参数声明(13.2)不允许推导参数类型,则程序格式错误。
我们的模板参数包含一个占位符类型auto
,因此参数的类型是为发明声明中的变量x
推导出的类型auto x = s;
在这种情况下,参数的类型是S
,并且S
是参数声明的允许类型,因为S
是结构文字类型。
非类型模板参数的模板参数应是模板参数类型的转换常量表达式(7.7)。
这意味着模板参数s
应该是一个转换后的常量表达式。 所以根据[expr.const]/10 :
T
T
的表达式,其中转换后的表达式是常量表达式,并且隐式转换序列仅包含
- [..]
- (10.2) — 左值到右值的转换 (7.3.2)
- [..]
在应用任何隐式转换之前,我不确定左值s
是否被转换为纯右值。 注意 C++14 中转换后的常量表达式的定义是相对变化的。 N4140 §5.19 [expr.const]/3:(强调我的)
T
类型的转换常量表达式是一个表达式,隐式转换为T
类型的纯右值,其中转换后的表达式是核心常量表达式,并且隐式转换序列仅包含 [..]
因此,根据 C++14,保证转换后的表达式在应用任何隐式转换之前转换为纯右值。
我不太确定左值到右值的转换是否应用于 template-argument s
。 换句话说,我不确定左值s
是否被转换为纯右值。
所以一般来说,如果我将左值作为非类型模板参数的模板参数传递,左值到右值的转换是否应用于该左值?
除此之外,object s
常量初始化了吗?
[expr.const]/10 的含义是,每当一个表达式出现在需要“类型T
的转换常量表达式”的上下文中时,该表达式就“隐式转换为T
类型”以及 [expr.const 中的附加限制]/10 应适用。
所以s
没有“在应用任何隐式转换之前转换为纯右值”。 相反, s
被隐式转换为类型S
,并且这种隐式转换可能涉及一些标准和/或用户定义的转换,例如左值到右值的转换。 要知道它是否存在,我们必须查看 [conv.general] 中的隐式转换规则。 特别是[conv.general]/6指出
任何隐式转换的效果都与执行相应的声明和初始化,然后使用临时变量作为转换的结果相同。 如果
T
是左值引用类型或对 function 类型 ([dcl.ref]) 的右值引用,则结果是左值,如果T
是对 object 类型的右值引用,则结果是 xvalue,否则是纯右值。 当且仅当初始化使用它作为一个glvalue时,表达式E被用作一个glvalue。
我相信这里的措辞是旧标准版本的遗物。 它应该说的(用现代语言)是,当T
是非引用类型时,将E隐式转换为T
会产生一个纯右值,它会初始化其结果 object (称为t
),就好像通过T t = E;
.
所以我们认为S t = s;
这个初始化有什么作用? 它调用S
的复制构造函数来初始化t
; [dcl.init.general]/16.6.2.1 。 在这种情况下,没有左值到右值的转换。 (class 类型上的左值到右值转换很少见,但确实发生在语言的某些地方,例如[expr.call]/12 , [expr.cond]/7 。如果左值到右值转换是在s
上执行,它也会调用复制构造函数; [conv.lval]/3.2 。但在这种特殊情况下,语言的规则不需要左值到右值的转换。)
因此,使用s
作为S
类型模板参数的模板参数的结果是生成了一个纯右值,它通过调用以s
作为参数的复制构造函数来初始化其结果 object。 (这个特定的复制构造函数被隐式定义为不做任何事情,因为没有要复制的子对象。)
这回答了您关于是否应用左值到右值转换的问题。 (您可能想知道纯右值实际发生了什么,以及复制构造函数是否实际被调用,但这是一个单独的主题,我现在不想讨论,因为它会使这个答案太长。)
至于s
是否是常量初始化的,是的。 它满足 (2.1)(因为它有一个初始化器)和 (2.2),因为它的初始化的完整表达式是一个常量表达式(没有什么可以阻止它成为一个常量表达式,因为它除了调用constexpr
什么都不做默认构造函数,它本身什么都不做)。 不过,我不确定这与您的其他问题有什么关系。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.