C++ std::abs可用于constexpr函数,但前提是它';它是模板。为什么?
标准中假定C++ std::abs可用于constexpr函数,但前提是它';它是模板。为什么?,c++,constexpr,function-templates,C++,Constexpr,Function Templates,标准中假定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; } 模板 常量表达式TF
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;
}
模板
常量表达式TF(常量输入){
返回标准::abs(输入);
}
int main(){
int i=-1;
int a=f(i);
返回0;
}
守则:
- 无论是否使用模板,使用GCC都可以很好地编译
- 它在叮当声中不起作用
- 在VisualStudio中,它使用模板行进行编译,但在没有模板的情况下编译失败
std::abs时出错的原因。gcc的行为基于其将std::abs
实现为constepr
的决策,顺便说一下
对于模板函数编译器无法知道是否可以在编译时计算内部代码,因为它可能基于模板参数的实际类型,并调用不同的函数重载。虽然大多数编译器会决定不检查std::abs
的所有可能的重载是否都不能constepr
,从而让代码通过编译,但理论上编译器可以检查(在可以检查的非常特定的情况下,如本例)由于不允许用户通过添加新版本的abs
来扩展std
(允许扩展到std
的列表由规范关闭),因此可以看到函数永远不能是constepr
,从而生成编译错误。但是,在更一般的情况下,如果所有可能的情况下都无法生成constepr
函数,则编译器无法检查模板函数,因为每次调用模板函数时,它只能看到内部调用的可用重载,并且内部调用可能有其他可用重载,在别处调用模板时
请注意,将constexpr
函数作为模板,只是为了编译它,并不是一个好方法。如果函数是constexpr
(即可以在编译时调用),则实际决定将基于实际调用,如果在所有情况下函数都不能是constexpr
,则您试图以某种方式欺骗编译器,但最终欺骗的主要是您自己
顺便说一下,在我对clang 10.1和trunk版本的检查中,我没有发现模板版本的编译错误,以下代码:
即使constexpr
模板函数中的内部调用不依赖于模板参数,gcc和clang似乎都不会生成错误:
int-myabs(){
返回42;
}
模板
constexpr int f(){
//这从来不是一个上下文
//但gcc和clang对此没有意见
返回myabs();
}
同样,这是允许的,因为不符合constexpr
模板功能不需要诊断:
:
[…]如果模板的专门化在被视为非模板函数时不能满足constexpr函数的要求,则模板的格式不正确,不需要诊断
不,它确实有效!甚至在调用时。我已经有一段时间不知情地使用了这个“把戏”。这两种情况都有投诉。这是否回答了您的问题?尝试在MSVC中将constexpr
添加到您的i
和a
-它是否编译?如果不是,那么f
就不是一个真正的constexpr
函数。“我的意思是,如果它像那样编译,它仍然是constexpr”-不,它不是这个意思。声明为constexpr
的模板“退回”为非constexpr如果实例化不是constexpr在第二种情况下,函数模板是格式错误的NDR。没有一组模板参数会产生实际的constexpr函数(直到std::abs
正式成为constexpr)。我一直认为作为constexpr成功编译意味着它至少在理论上可以在编译时运行?这个假设是错误的吗?@Basti对于模板函数,编译器没有义务检查所有可能的重载,并确保它永远不会是constexpr,在大多数情况下,它实际上无法执行该检查,因为每次调用模板函数时,它只会看到可用的重载,而在其他地方调用模板时,内部调用可能会有其他可用的重载。很少有情况可以检查它,例如直接调用std::函数时,但编译器也不必检查。@StoryTeller UnslanderMonica会将它添加到答案中,谢谢。
template<typename T>
constexpr T myabs(T t) {
return std::abs(t);
}
int main() {
int i = myabs(3);
}
int main() {
constexpr int i = myabs(3);
}
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();
}