MSVC can't compile the below code (https://godbolt.org/z/feenYcaen):
error C2131: expression did not evaluate to a constant
Line 10 comes from MSVC's own offsetof
. I think this statement is a constant expression, even if it's not, can't MSVC compute this at runtime? Is this an MSVC bug?
#include <type_traits>
#include <iostream>
struct A{};
template<typename T, A T::*MPtr>
struct KKP {
private:
static const std::uintptr_t c =
((size_t) & reinterpret_cast<char const volatile&>((((T*)0)->*MPtr)));
};
struct PC {
A t;
};
size_t f() {
KKP<PC, &PC::t> ar;
}
In fact, I solved this by using a member function instead of a class member (https://godbolt.org/z/c65eEnvd7), but then MSVC becomes more confusing for me:
#include <type_traits>
#include <iostream>
struct A{};
template<typename T, A T::*MPtr>
struct KKP {
private:
static constexpr size_t c() {
return ((::size_t) & reinterpret_cast<char const volatile&>((((T*)0)->*MPtr)));
}
};
struct PC {
A t;
};
size_t f() {
KKP<PC, &PC::t> ar;
return 0;
}
offsetof cannot be implemented in standard C++ and requires compiler support - cppreference
From the quote, it immediately follows that both code samples do not conform to the C++ standard.
There are null pointer dereference undefined behavior and reinterpret_cast
conversion in the expression that is supposed to be constexpr
.
Both do not belong to core constant expressions:
A core constant expression is any expression whose evaluation would not evaluate any one of the following:
8. an expression whose evaluation leads to any form of core language undefined behavior ...
18. reinterpret_cast
The problem still persists in the second code sample because the function is not in fact constexpr
, and the program is ill-formed.
A constexpr function must satisfy the following requirements:
- there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression ... (until C++23)
For constexpr function templates and constexpr member functions of class templates, at least one specialization must satisfy the abovementioned requirements. Other specializations are still considered as constexpr, even though a call to such a function cannot appear in a constant expression. 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 (until C++23). cppreference
Consider example
#include <type_traits>
#include <iostream>
struct A{};
template<typename T, A T::*MPtr>
class KKP {
public: // make the function public
static constexpr size_t c() {
return ((::size_t) & reinterpret_cast<char const volatile&>((((T*)0)->*MPtr)));
}
};
struct PC {
A t;
};
size_t f() {
constexpr auto foo = KKP<PC, &PC::t>::c(); // fails to evaluate in the constexpr context
return 0;
}