See the code below, for clang (14.0.6), almost all member function calling via pointer were compile-error, except calling by 0x0. But GCC would accept more.
It seems lacks useful information on cppreference.(https://en.cppreference.com/w/cpp/language/constant_expression)
How can I call constexpr member function and constexpr conversion function by pointer?
#include <memory>
struct MyType {
constexpr static bool fixed() { return true; }
constexpr bool dyn() const { return true; }
constexpr MyType() {}
};
int main() {
std::unique_ptr<MyType> uptr;
MyType inst;
MyType *ptr;
MyType &ref = inst;
static_assert( (*(const MyType* const)0).fixed() ); //clang_Pass!
static_assert( (*(const MyType* const)1).fixed() ); //clang_error
static_assert(uptr->fixed()); //error
static_assert(uptr->dyn()); //error
static_assert(inst.fixed()); //Pass!
static_assert(inst.dyn()); //Pass!
static_assert(ptr->fixed()); //GCC_Pass! clang_error
static_assert(ptr->dyn()); //error
static_assert(ref.fixed()); //GCC_Pass! clang_error
static_assert(ref.dyn()); //error
return 0;
}
(*(const MyType* const)0).fixed()
Has defined behavior and is a constant expression, because 0 is a null pointer constant, so that this resolves to a static_cast, not reinterpret_cast. Therefore none of the disqualifiers in [expr.const]/5 apply. Clang is correct to accept the static_assert and GCC should also accept it. This wouldn't work if you retrieved 0 from a variable or anything but using the literal 0 directly in the cast.
(*(const MyType* const)1).fixed()
Behavior of the cast is implementation-defined, but in either case, it uses reinterpret_cast which is not allowed in constant expressions.
uptr->fixed()
You default-initialized uptr which means it is empty. operator-> of std::unique_ptr has a pre-condition that it is not empty. Therefore this has undefined behavior. It is unspecified whether library undefined behavior causes an otherwise constant expression to not be a constant expression. So it is unspecified whether compilation will fail with the static_assert.
uptr->dyn()
Same.
inst.fixed()
That has defined behavior and is a constant expression, because none of the disqualifiers in [expr.const]/5 apply.
inst.dyn()
Same.
ptr->fixed()
That's UB for dereferencing a pointer with indeterminate value and therefore also not a constant expression. GCC is wrong to accept it in the static_assert.
ptr->dyn()
Same.
ref.fixed()
Defined behavior, but not a constant expression until recently because ref isn't usable in constant expressions, as it hasn't been initialized with to refer to an object of static storage duration. That's specifically a requirement when using a reference's name in a constant expression. (see [expr.const]/5.12)
This was recently improved in the standard as a defect report against older versions, but compilers haven't implemented it yet as far as I know. I think (should probably check) this should work with that defect report implemented. I guess GCC passing it was previously a bug, not due to implementation of the DR.
ref.dyn()
Same.
How can I call constexpr member function and constexpr conversion function by pointer?
The pointer variable needs to be marked constexpr and initialized to refer to an object with static storage duration or the pointer's lifetime must start during evaluation of the constant expression. In the case of fixed it should also be fine to mark the pointer constexpr and initialize it with a null pointer constant, preferably nullptr over a literal 0.
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