Search code examples
c++c++11endianness

constexpr and endianness


A common question that comes up from time to time in the world of C++ programming is compile-time determination of endianness. Usually this is done with barely portable #ifdefs. But does the C++11 constexpr keyword along with template specialization offer us a better solution to this?

Would it be legal C++11 to do something like:

constexpr bool little_endian()
{
   const static unsigned num = 0xAABBCCDD;
   return reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD;
}

And then specialize a template for both endian types:

template <bool LittleEndian>
struct Foo 
{
  // .... specialization for little endian
};

template <>
struct Foo<false>
{
  // .... specialization for big endian
};

And then do:

Foo<little_endian()>::do_something();

Solution

  • Assuming N2116 is the wording that gets incorporated, then your example is ill-formed (notice that there is no concept of "legal/illegal" in C++). The proposed text for [decl.constexpr]/3 says

    • its function-body shall be a compound-statement of the form { return expression; } where expression is a potential constant expression (5.19);

    Your function violates the requirement in that it also declares a local variable.

    Edit: This restriction could be overcome by moving num outside of the function. The function still wouldn't be well-formed, then, because expression needs to be a potential constant expression, which is defined as

    An expression is a potential constant expression if it is a constant expression when all occurrences of function parameters are replaced by arbitrary constant expressions of the appropriate type.

    IOW, reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD would have to be a constant expression. However, it is not: &num would be a address constant-expression (5.19/4). Accessing the value of such a pointer is, however, not allowed for a constant expression:

    The subscripting operator [] and the class member access . and operators, the & and * unary operators, and pointer casts (except dynamic_casts, 5.2.7) can be used in the creation of an address constant expression, but the value of an object shall not be accessed by the use of these operators.

    Edit: The above text is from C++98. Apparently, C++0x is more permissive what it allows for constant expressions. The expression involves an lvalue-to-rvalue conversion of the array reference, which is banned from constant expressions unless

    it is applied to an lvalue of effective integral type that refers to a non-volatile const variable or static data member initialized with constant expressions

    It's not clear to me whether (&num)[0] "refers to" a const variable, or whether only a literal num "refers to" such a variable. If (&num)[0] refers to that variable, it is then unclear whether reinterpret_cast<const unsigned char*> (&num)[0] still "refers to" num.