Take: template <bool> struct Z { }; struct C { void f(); Z<&C::f == nullptr> z; }; ---- CUT --- This should be accepted in C++11 or newer.
The following program compiles. https://godbolt.org/z/aTvchYxYW ``` struct C { void f() {} static_assert(__builtin_constant_p(&C::f)); static_assert(!__builtin_constant_p(&C::f == nullptr)); // not nonzero yet }; static_assert(__builtin_constant_p(&C::f == nullptr)); // nonzero now struct D { void f() {} static_assert(__builtin_constant_p(&C::f == nullptr)); static_assert(!__builtin_constant_p(&D::f == nullptr)); }; static_assert(__builtin_constant_p(&C::f == nullptr)); static_assert(__builtin_constant_p(&D::f == nullptr)); ``` Looks that the `&C::f` is known to be constexpr right after the function was parsed. But only when the class completely parsed, its value was assigned. We can then compare it to nullptr. To make code in comment0 accepted, we need some kind of `not null' mark on the expression tree. 0ne possible way is to assign the `&C::f` in advance, right after it was parsed.
The root cause for this is that the compiler forbids constant folding when involving PMF of an incomplete class. https://gcc.gnu.org/git?p=gcc.git;a=blob;f=gcc/cp/expr.c;h=d16d1896f2ddd08264b389b02b9640cca332ec13;hb=refs/heads/master#l42 (gcc/cp/expr.c) > 42 /* We can't lower this until the class is complete. */ > 43 if (!COMPLETE_TYPE_P (DECL_CONTEXT (member))) > 44 return cst; If we comment this `if`, the constant folding will succeed at (gcc/cp/expr.c) > 67 expand_ptrmemfunc_cst (cst, &delta, &pfn); > 68 cst = build_ptrmemfunc1 (type, delta, pfn); solving everything.
By the way, in the current design, the class definition is passed twice in order we can see every member data/function declaration before parsing NSDMI and member functions. The class is complete after parsing all declaration, which means `&C::f == nullptr` can reduce to false since that. So, under current design, the following code compiles on GCC. https://godbolt.org/z/fMTsf4KoM ``` struct C { C() { static_assert(&C::f != 0); // complete type } void f() noexcept(&C::f != 0) { static_assert(&C::f != 0); // complete type } static_assert(__builtin_constant_p(&C::f)); // incomplete type static_assert(!__builtin_constant_p(&C::f == 0)); // incomplete type }; static_assert(&C::f != 0); // complete type ```
Note completeness of the class does not matter either: template <bool> struct Z { }; struct C { void f(); }; static_assert ((&C::f == 0) == false,""); Z<&C::f == 0> z; --- CUT --- The static_assert passes just fine but the template is still rejected.