c++ - std::abs 可以在 constexpr 函数中使用,但前提是它是模板化的。为什么?
问题描述
据说std::abs
不在constexpr
标准中(即使在 C++20 中)。但在实践中,我发现我可以constexpr
在函数模板化的非常特殊的条件下编译它。请参阅这个完全有效的示例:
template<class T>
constexpr T f(const T input) {
return std::abs(input);
}
int main() {
int i = -1;
int a = f(i);
return 0;
}
编码:
- 使用 GCC 编译良好,有和没有模板。
- 它在 Clang 中不起作用。
- 在 Visual Studio 中,它使用模板行编译,但没有模板编译失败。
解决方案
对于常规函数,编译器可能会根据函数参数的类型知道内部代码是否可以在编译时进行评估。这就是您在调用std::abs
MSVC和clang时收到错误的原因。gcc的行为是基于其实施的决定std::abs
,constexpr
顺便说一句,这是一个有问题的决定。
对于模板函数,编译器无法知道内部代码是否可以在编译时评估,因为它可能基于模板参数的实际类型,并调用不同的函数重载。虽然大多数编译器会决定不检查是否所有可能的重载std::abs
cannot be constexpr
,从而让代码通过编译,但理论上编译器可以检查(在可以检查的非常特殊的情况下,比如这个),并且因为不允许用户std
通过添加新版本来扩展abs
(允许的扩展列表std
已被规范关闭),可以看到该函数永远不会constexpr
从而产生编译错误。然而,在更一般的情况下,如果所有可能的情况都无法生成函数,编译器无法检查模板函数constexpr
,因为每次调用模板函数时,它只会看到内部调用的可用重载,并且可能还有其他可用的重载当模板在别处调用时,内部调用的重载。
请注意,将constexpr
函数设为模板以使其可以编译,这不是一个好方法。函数是否是constexpr
(即可以在编译时调用)的实际决定将基于实际调用,并且如果在所有情况下该函数都不是constexpr
你试图以某种方式欺骗编译器但最终主要是在欺骗你自己。 ..
顺便说一句,在我对clang 10.1 和 trunk 版本的检查中,我没有在模板版本上遇到编译错误,此代码同时使用 gcc 和 clang 编译:
template<typename T>
constexpr T myabs(T t) {
return std::abs(t);
}
int main() {
int i = myabs(3);
}
虽然这使用 gcc 编译(实现std::abs
as constexpr
)并因 clang 而失败:
int main() {
constexpr int i = myabs(3);
}
即使模板函数内的内部调用不依赖于模板参数并且永远不可能是常量表达式, gcc和clang似乎都不会产生错误:constexpr
int myabs() {
return 42;
}
template<class T>
constexpr int f() {
// this is never a contexpr
// yet gcc and clang are ok with it
return myabs();
}
constexpr
同样,这是允许的,因为不符合标准的模板函数不需要诊断:
[dcl.constexpr] 9.2.5/7 - constexpr 和 consteval 说明符:
[...] 如果模板的特殊化在被视为非模板函数时满足 constexpr 函数的要求,则模板格式错误,不需要诊断。
推荐阅读
- ios - 使用动画显示颜色序列 (Swift)
- angular - 带有 ngFor 的 Angular Flex 布局,每行 X 个元素
- r - 使用关键字频率对共现进行加权
- spring-boot - 应用程序在第二次启动时启动两次而没有环境属性
- css - 响应式选择框
- codenameone - 用于处理 TextField 和 TextArea 的验证器
- powershell - 使用 CSV 自动删除边界 - Remove-CMBoundary
- android - 如何从我的 Android 应用程序将图像分享到 Snapchat?
- matlab - 将 xlsm 文件读入 MATLAB | 错误文件名必须是字符串
- c++ - 需要帮助解码此 typedef