Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is difference of two constexpr instances of __func__ pointers still constexpr?

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__);
}
like image 337
Ayxan Haqverdili Avatar asked Dec 28 '19 16:12

Ayxan Haqverdili


People also ask

Can you change constexpr?

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.

Can constexpr functions call non constexpr functions?

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.

Is constexpr constant?

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.

Is constexpr always evaluated at compile time?

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.


1 Answers

__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 form

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

like image 133
Nicol Bolas Avatar answered Oct 12 '22 14:10

Nicol Bolas