std::abs can be used in constexpr function, but only if it's templated. Why?

谁说我不能喝 提交于 2020-12-13 03:09:19

问题


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;
}

The code:

  • Compiles fine with GCC, with and without the template.
  • It doesn't work in Clang.
  • And in Visual Studio it compiles with the template line, but fails compilation without the template.

回答1:


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.

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.


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...


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);
}

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

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

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();
}

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

[dcl.constexpr] 9.2.5/7 - The constexpr and consteval specifiers:

[...] 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.



来源:https://stackoverflow.com/questions/64220243/stdabs-can-be-used-in-constexpr-function-but-only-if-its-templated-why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!