问题
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