在没有constexpr的模板非类型参数处进行类型转换
Type conversion at template non-type argument without constexpr
考虑以下代码:
struct A {
constexpr operator int() { return 42; }
};
template <int>
void foo() {}
void bar(A a) {
foo<a>();
}
int main() {
foo<A{}>();
const int i = 42;
foo<i>(); // (1)
A a{};
static_assert(i == a, "");
bar(a);
foo<a>(); // error here
}
带有c++14的Clang 3.7接受这一点,而带有c++14的gcc 5.2.0则不接受,生成以下消息:
/tmp/gcc-explorer-compiler1151027-68-1f801jf/example.cpp: In function 'int main()': 26 : error: the value of 'a' is not usable in a constant expression foo<a>(); ^ 23 : note: 'a' was not declared 'constexpr' A a{}; ^ Compilation failed
按照gcc的建议将a
更改为constexpr
可以修复gcc编译错误,但如果没有constexpr
,哪个编译器是对的?
对我来说,似乎a
应该是"可用于常量表达式",就像static_assert
一样。此外,i
可以以相同的方式使用(标记为(1)
),以及bar()
编译的事实,也让我认为gcc是错误的。
UPD:报告了一个针对gcc的错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68588
[expr.const]/(4.1)允许用户定义的转换,在[expr.const]/2中我看不到任何一个适用的要点会阻止您的表达式成为常量。事实上,这些要求非常宽松,以至于将a
声明为
A a;
即使a
没有constexpr
默认构造函数等,它仍然给出了一个格式良好的程序,因为转换运算符是constexpr
,并且没有评估任何成员。
正如您自己所看到的,GCC是矛盾的,因为它允许static_assert
条件中的a
,但不允许模板参数。
我认为Clang是正确的。
当前C++(n4296)的草案说:
14.3.2模板非类型参数[temp.arg.notype]
非类型模板参数的模板参数应为的转换常量表达式(5.20)模板参数的类型
5.20§4说(强调我的):
5.20常量表达式[expr.const]
(4) T类型的转换常量表达式是一个隐式转换为T类型的表达式,其中表达式是一个常量表达式,并且隐式转换序列仅包含
(4.1)--用户定义的转换。。。
foo<a>();
a中的IFAIK通过constexpr用户定义转换转换为int,因此是转换后的常量表达式。
话虽如此,我们离边缘案例不远了,我的建议是:不要在生产代码中使用这样的构造:-)
As@Jarod42建议a
应为constexpr
。这是因为模板是在编译时推导的,因此非类型参数也必须在编译时可用。constexpr
承诺它们将在编译时可用。
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 将内置类型变量传递给只有一个类类型参数的"+"运算符函数时自动类型转换的构造函数
- 如何定义依赖于参数包转换的函数的返回类型
- C++stoi:这两个重载都无法转换所有参数类型
- C++ 按非类型参数类型划分的模板专用化
- 我可以将C 17 Capture lambda ConstexPR转换操作符的结果用作函数指针模板非类型参数吗?
- 模板参数类型通过转换操作员扣除
- 转换具有不同参数类型的二进制运算函数
- 为什么指针类型强制转换不适用于模板非类型参数
- 3个重载都无法转换所有参数类型(在MFC/C++项目中)
- 转换和模板类型参数
- 移动语义:从"类型&&"到"类型"的转换无效。模板:将未知参数传递给重载函数
- 错误 C2665:'cv::fillPoly':2 个重载都无法转换所有参数类型
- 强制转换函数指针,该指针因参数类型而异
- 为什么这个函数重载与参数类型转换不明确
- 生成错误:12 个重载中没有一个可以转换所有参数类型
- 将命令行参数解析/转换为不同类型
- 没有已知的参数从类型到类型的转换
- 模板类型参数类型