Is this valid C++?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
GCC and MSVC think it's OK, Clang thinks it's not: Compiler Explorer.
All compilers agree that this one is OK: Compiler Explorer.
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
Clang again doesn't like this one, but the others are OK with it: Compiler Explorer
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
What is up here? I think arithmetic on unrelated pointers is undefined behavior but __func__
returns the same pointer, no? I am not sure, so I thought I may test it. If I recall correctly, std::equal_to
can compare unrelated pointers without undefined behavior:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Clang thinks eq(__func__, __func__)
isn't a constant expression, even though std::equal_to::operator()
is constexpr. Other compilers don't complain: Compiler Explorer
Clang won't compile this one either. Complains that __func__ == __func__
is not a constant expression: Compiler Explorer
int main() {
static_assert(__func__ == __func__);
}
A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.
A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.
The keyword constexpr was introduced in C++11 and improved in C++14. It means constant expression. Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors.
constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well.
__func__
in C++ is an identifier. In particular, it references a specific object. From [dcl.fct.def.general]/8:
The function-local predefined variable
__func__
is defined as if a definition of the formstatic const char __func__[] = "function-name";
had been provided, where function-name is an implementation-defined string. It is unspecified whether such a variable has an address distinct from that of any other object in the program.
As a function-local predefined variable, this definition (as if) appears at the beginning of the function block. As such, any uses of __func__
within that block will refer to that variable.
As for the "any other object" part, a variable defines an object. __func__
names the object defined by that variable. Therefore, within a function, all uses of __func__
name the same variable. What is undefined is whether that variable is a distinct object from other objects.
That is, if you're in a function named foo
, and you used the literal "foo"
somewhere else in the problem, it is not forbidden for an implementation to have the variable __func__
also be the same object that the literal "foo"
returns. That is, the standard doesn't require that every function in which __func__
appears must store data separate from the string literal itself.
Now, C++'s "as if" rule allows implementations to deviate from this, but they cannot do it in a way that would be detectable. So, while the variable itself may or may not have a distinct address from other objects, uses of __func__
in the same function must behave as if they are referring to the same object.
Clang does not seem to implement __func__
this way. It appears to implement it as if it returned a prvalue string literal of the function's name. Two distinct string literals don't have to refer to the same object, so subtracting pointers to them is UB. And undefined behavior in a constant expression context is ill-formed.
The only thing that makes me hesitant to say that Clang is 100% wrong here is [temp.arg.nontype]/2:
For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
...
- a predefined
__func__
variable.
See, this seems to allow some fudging by the implementation. That is, while __func__
can technically be a constant expression, you can't use it in a template parameter. It is treated like a string literal, even though it is technically a variable.
So on some level, I would say that the standard is talking out of both sides of its mouth.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With