Search code examples
c++gccc++11clangconstexpr

constexpr and initialization of a static const void pointer with reinterpret cast, which compiler is right?


Consider the following piece of code:

struct foo {
  static constexpr const void* ptr = reinterpret_cast<const void*>(0x1);
};

auto main() -> int {
  return 0;
}

The above example compiles fine in g++ v4.9 (Live Demo), while it fails to compile in clang v3.4 (Live Demo) and generates the following error:

error: constexpr variable 'ptr' must be initialized by a constant expression

Questions:

  • Which of the two compilers is right according to the standard?

  • What's the proper way of declaring an expression of such kind?


Solution

  • TL;DR

    clang is correct, this is known gcc bug. You can either use intptr_t instead and cast when you need to use the value or if that is not workable then both gcc and clang support a little documented work-around that should allow your particular use case.

    Details

    So clang is correct on this one if we go to the draft C++11 standard section 5.19 Constant expressions paragraph 2 says:

    A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]

    and includes the following bullet:

    — a reinterpret_cast (5.2.10);

    One simple solution would be to use intptr_t:

    static constexpr intptr_t ptr = 0x1;
    

    and then cast later on when you need to use it:

    reinterpret_cast<void*>(foo::ptr) ;
    

    It may be tempting to leave it at that but this story gets more interesting though. This is know and still open gcc bug see Bug 49171: [C++0x][constexpr] Constant expressions support reinterpret_cast. It is clear from the discussion that gcc devs have some clear use cases for this:

    I believe I found a conforming usage of reinterpret_cast in constant expressions useable in C++03:

    //---------------- struct X {  X* operator&(); };
    
    X x[2];
    
    const bool p = (reinterpret_cast<X*>(&reinterpret_cast<char&>(x[1]))
    - reinterpret_cast<X*>(&reinterpret_cast<char&>(x[0]))) == sizeof(X);
    
    enum E { e = p }; // e should have a value equal to 1
    //----------------
    

    Basically this program demonstrates the technique, the C++11 library function addressof is based on and thus excluding reinterpret_cast unconditionally from constant expressions in the core language would render this useful program invalid and would make it impossible to declare addressof as a constexpr function.

    but were not able to get an exception carved for these use cases, see closed issues 1384:

    Although reinterpret_cast was permitted in address constant expressions in C++03, this restriction has been implemented in some compilers and has not proved to break significant amounts of code. CWG deemed that the complications of dealing with pointers whose tpes changed (pointer arithmetic and dereference could not be permitted on such pointers) outweighed the possible utility of relaxing the current restriction.

    BUT apparently gcc and clang support a little documented extension that allows constant folding of non-constant expressions using __builtin_constant_p (exp) and so the following expressions is accepted by both gcc and clang:

    static constexpr const void* ptr = 
      __builtin_constant_p( reinterpret_cast<const void*>(0x1) ) ? 
        reinterpret_cast<const void*>(0x1) : reinterpret_cast<const void*>(0x1)  ;
    

    Finding documentation for this is near impossible but this llvm commit is informative with the following snippets provide for some interesting reading:

    support the gcc __builtin_constant_p() ? ... : ... folding hack in C++11

    and:

    // __builtin_constant_p ? : is magical, and is always a potential constant.
    

    and:

    // This macro forces its argument to be constant-folded, even if it's not
    // otherwise a constant expression.
    #define fold(x) (__builtin_constant_p(x) ? (x) : (x))
    

    We can find a more formal explanation of this feature in the gcc-patches email: C constant expressions, VLAs etc. fixes which says:

    Furthermore, the rules for __builtin_constant_p calls as conditional expression condition in the implementation are more relaxed than those in the formal model: the selected half of the conditional expression is fully folded without regard to whether it is formally a constant expression, since __builtin_constant_p tests a fully folded argument itself.