考虑最小的例子:
template <int>
struct bar { };
int main()
{
[](auto i) -> bar<i> { return {}; };
}
甚至:
template <int>
struct bar {};
template <class I>
auto foo(I i) -> bar<i> {}
Clang编译这两种形式没有问题,但GCC发现用法无效(例1),(例2)
这个问题可能看起来很愚蠢,但是参数的类型可能会使 constexpr 转换运算符重载(在这种情况下,从传递给 lambda/foo 的值推导出的i
类型以 constexpr 方式int
),在这种情况下,不被迫做一些解决方法来直接访问它会非常方便......
这似乎是一个gcc
错误。我将其报告为问题#80242。
gcc
抱怨i
作为模板参数的有效性:
错误:模板参数 1 无效
我遵循了从trailing-return-type
到template-argument
的C++语法,这需要是一个constant-expression
:
模板参数:
- 常量表达式<->
- 类型 ID
- 身份表达式
那么真正的问题变成了:"i
是一个有效的constant-expression
吗?
我认为答案是肯定的,因为§8.20.4[expr.const]说:
类型为T
的转换常量表达式是隐式转换为类型
T
的表达式,其中转换后的表达式是常量表达式,隐式转换序列仅包含:
- 用户定义的转换,
[...]
(注意:此类表达式可用于新表达式、大小写表达式、枚举器初始值设定项(如果基础类型是固定的)、数组边界和非类型模板参数。
有一系列隐式转换,从i
开始,将产生一个转换后的常量表达式,这是一个常量表达式。鉴于:
template <int>
struct bar { };
template <class I>
auto foo(I i) -> bar<i> { }
int main()
{
foo(std::integral_constant<int, 1>{}); // (0)
}
在(0)处的函数调用上下文中,参数
i
是std::integral_constant<int, 1>
的实例。std::integral_constant
提供对基础value_type
的constexpr
用户定义转换。
转换后的常量表达式明确允许用户定义的转换,如上文§8.20.4[expr.const]所示。
std::integral_constant::operator value_type()
将返回非类型模板参数1
。这是一个核心常量表达式,因为它不违反§8.20.2[expr.const]中指定的任何规则。因此,转换后的常量表达式是常量表达式。
我相信你的两个例子都是错误的。基于标准措辞5.20 常量表达式 [expr.const]/p2.7:
2条件表达式 e 是核心常量表达式 除非评估E,遵循摘要的规则 机器 (1.9),将计算以下表达式之一:j
。
- 左值到右值的转换 (4.1),除非它适用于:
函数参数不能是常量表达式,因为为了将它们作为模板参数传递,您需要将 lvalue 应用于它们的 rvalue 转换。
在CLANG和GCC中,如果您输入模板,您将根据上述措辞收到错误。我相信,由于在示例中没有模板是必需的,因此两个编译器都是正确的。