Strange behavior with recursive C ++ patterns when C ++ 11 is enabled

I am trying to understand some kind of recursive code for the C ++ code that was passed to me, and I hit some strange behavior. For some reason, the compiler seems to be able to add two values ​​at compile time, but left shift execution should be left to run time. And even then the problem arises only if I try to build with C ++ 11 turned on.

The code (which I have welded, and you will see below) defines two pairs of templates - one pair with the name shft and shft_aux and one pair with the name add and add_aux that generate themselves recursively. BTW, the add pattern should not be useful, its sole purpose is to demonstrate the problem, and not generate the actual value of min .

If I compile this code without command line options, it compiles just fine. But if I specify -std=c++11 -stdlib=libc++ , the static_assert in add_aux is still fine, but the static_assert on shft_aux now generates a compile-time error, saying static_assert expression is not an integral constant expression .

Why is the left shift handled differently than adding?

Thank you, Chris

ps I am using clang ++ version Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)

 #include <climits> template <unsigned size> struct shft; // forward template <unsigned size> struct shft_aux { static const int min = shft<size>::min; }; template <unsigned size> struct shft { typedef shft_aux<size - 1> prev; static const int min = prev::min << CHAR_BIT; }; // Base specialization of shft, puts an end to the recursion. template <> struct shft<1> { static const int min = SCHAR_MIN; }; // ----- template <unsigned size> struct add; // forward template <unsigned size> struct add_aux { static const int min = add<size>::min; }; template <unsigned size> struct add { typedef add_aux<size - 1> prev; static const int min = prev::min + CHAR_BIT; }; // Base specialization of add, puts an end to the recursion. template <> struct add<1> { static const int min = SCHAR_MIN; }; // ----- int main() { static_assert(shft_aux<sizeof(int)>::min < 0, "min is not negative"); static_assert(add_aux<sizeof(int)>::min < 0, "min is not negative"); return 0; } 
+6
source share
1 answer

C ++ 11 Standard, [expr.shift] / 2

The value of E1 << E2 is E1 left-shifted position of E2 ; freed bits are filled with zeros. If E1 is of unsigned type, [...]. Otherwise, if E1 has a signed type and a non-negative value, and E1 * 2 E2 is represented in the result type, then this is the resulting value; , undefined behavior .

[emphasis mine]

This slightly affects DR1457 , which leads to a transition to a certain sign behavior:

Otherwise, if E1 has a signed type and a non-negative value, and E1 * 2 E2 is represented in the corresponding unsigned type of the result type [...].

In any case, in OP, E1 is negative, so there is still Undefined Behavior. Thus, inside constant expressions it is not allowed:

[expr.const] / 2 A conditional expression is an expression of a basic constant if it is not associated with one of the following as a potentially evaluated subexpression [...]

  • [...]
  • a result that is not mathematically defined or not in the range of displayed values ​​for its type;

This point has changed ( DR1313 ); n3485 says:

  • an operation that will have Undefined behavior [Note: including, for example, a stream (section 5), certain pointer arithmetic (5.7), division by zero (5.6), or some shift operations (5.8) - the final note];

[class.static.data] / 3

If the non-volatile const static data element is of the type of an integral or enumeration, its declaration in the class definition may indicate a logical or equal-initializer, in which each initializer clause, which is an assignment, the expression is a constant expression


Conclusion: the SCHAR_MIN shift SCHAR_MIN not a constant expression, so you cannot do this in the class initializer in a static data element.

Tip: Always compile with -Wall -Wextra -pedantic . Without parameters, IMO is a bad idea for g ++ and compatible compilers. g ++ / clang ++ uses gnu99 mode by gnu99 ( see clang doc ), which is an extension to C ++ 98 AFAIK. In addition, you will miss many important warnings.

+7
source

All Articles