std :: abs可以在constexpr函数中使用,但前提是必须将其模板化.为什么? [英] std::abs can be used in constexpr function, but only if it's templated. Why?

查看:84
本文介绍了std :: abs可以在constexpr函数中使用,但前提是必须将其模板化.为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据说:std :: abs 不是标准中的 constexpr (即使在C ++ 20中也是如此).但是在实践中,我发现可以在该函数已模板化的非常特殊的条件下将其编译为 constexpr .请参阅此完全有效的示例:

Supposedly std::abs is not constexpr in the standard (even in C++20). But in practice I found out that I can compile it as constexpr under the very peculiar condition that the function is templated. See this completely working example:

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中,它使用模板行进行编译,但是在没有模板的情况下编译失败.

推荐答案

对于常规函数 ,编译器可能会根据函数参数的类型知道内部代码可以在编译时进行评估.这就是为什么在 MSVC clang 中调用 std :: abs 时会出错的原因. gcc 的行为基于其决定将 std :: abs 实现为 constexpr ,这是通过

For a regular function the compiler may know, based on the type of the function parameters, if the inner code can be potentially evaluated in compile time. This is why you get an error for calling std::abs in MSVC and clang. The behavior of gcc is based on its decision to implement std::abs as constexpr which is by the way a questionable decision.

对于模板函数 ,编译器无法知道内部代码是否可以在编译时进行评估,因为它可能基于模板参数的实际类型,具有不同功能的重载被调用.尽管大多数编译器会决定不检查 std :: abs 的所有可能的重载是否不能为 constexpr ,从而让代码通过编译,但从理论上讲,编译器可以进行检查(可以检查的特定情况,例如这种情况),并且由于不允许用户通过添加新版本的 abs ( std 已被规范关闭),可能会发现该函数永远不会是 constexpr ,从而产生编译错误.但是,在更一般的情况下,如果所有可能的情况都不能产生 constexpr 函数,则编译器将无法检查模板函数,因为每次对模板的调用都只能看到内部调用的可用重载.函数,并且在其他地方调用模板时,内部调用可能还有其他可用的重载.

For a template function the compiler cannot know if the inner code can be evaluated in compile time, as it may be based on the actual type of the template arguments, with different functions overload being called. While most compilers would decide not to check whether all possible overloads of std::abs cannot be constexpr, thus letting the code pass compilation, theoretically a compiler may check (in very specific cases that can be checked, like this one) and since the user is not allowed to extend std by adding a new version of abs (the list of allowed extensions to std is closed by the spec) it is possible to see that the function can never be constexpr and thus to generate a compilation error. In the more general case however, the compiler cannot check for a template function if all possible cases cannot produce a constexpr function, since it sees only the available overloads for the inner call, per each call to the template function, and there might be other available overloads for the inner call, when the template is called elsewhere.

请注意,将 constexpr 函数用作模板只是为了使其可以被编译,并不是一个好方法.函数是否为 constexpr (即可以在编译时调用)的实际决定将基于实际调用,并且如果在所有情况下函数均不能为 constexpr ,您正在尝试以某种方式欺骗编译器,但最终主要是自己欺骗...

Note that making a constexpr function a template, just so it can get compiled, would not be a good approach. The actual decision if the function is constexpr (i.e. can be called in compile time) would be based on the actual call, and if in all cases the function cannot be constexpr you are trying in a way to cheat the compiler but eventually are cheating mainly yourself...

顺便说一句,在对 clang 10.1和主干版本的检查中,模板版本未得到编译错误,此代码

By the way, in my check with clang 10.1 and trunk versions, I don't get compilation error on the template version, this code compiles both with gcc and clang:

template<typename T>
constexpr T myabs(T t) {
    return std::abs(t);
}

int main() {
    int i = myabs(3);
}

此方法使用gcc编译(将 std :: abs 实现为 constexpr ),并失败,但发出clang:

While this compiles with gcc (which implements std::abs as constexpr) and fails with clang:

int main() {
    constexpr int i = myabs(3);
}

即使 constexpr 模板函数内部的内部调用不依赖于 gcc clang ,似乎也不会产生错误.模板参数永远不能是常量表达式:

It seems that both gcc and clang do not generate an error even if the inner call inside a constexpr template function is not dependent on the template parameters and can never be a constant expression:

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 模板函数不需要诊断:

And again, this is allowed as no diagnostic is required for non-conforming constexpr template functions:

[dcl.constexpr] 9.2.5/7-constexpr和consteval说明符:

[...]如果在将模板视为非模板函数时,如果模板的任何专业化都不能满足constexpr函数的要求,则模板格式错误,无需诊断.

[...] If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required.

这篇关于std :: abs可以在constexpr函数中使用,但前提是必须将其模板化.为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆