I'm trying to understand some recursive C++ template code I've been handed, and I'm hitting some strange behavior. For some reason, the compiler seems able to add two values at compile-time but doing a left-shift has to be left for run-time. And even then, the problem only occurs if I try to build with c++11 enabled.
The code (which I've boiled down and you will see later) defines two pairs of templates - one pair named shft
and shft_aux
and one pair named add
and add_aux
that generate themselves recursively. BTW, the add
template is not supposed to be useful, its sole purpose is to demonstrate the problem, not to generate an actual min
value.
If I compile this code with no command-line parameters, it compiles just fine. But if I specify -std=c++11 -stdlib=libc++
, the static_assert on 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 left-shift treated differently from addition?
Thanks, Chris
p.s. I'm 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;
}
C++11 Standard, [expr.shift]/2
The value of
E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are zero-filled. IfE1
has an unsigned type, [...]. Otherwise, ifE1
has a signed type and non-negative value, and E1*2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
[emphasis mine]
This is affected slightly by DR1457 which makes shifting into the "sign-bit" defined behaviour:
Otherwise, if
E1
has a signed type and non-negative value, and E1*2E2 is representable in the corresponding unsigned type of the result type [...].
Anyway, in the OP, E1
is negative, so that's still Undefined Behaviour. As such, it is not allowed inside constant expressions:
[expr.const]/2 A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]
- [...]
- a result that is not mathematically defined or not in the range of representable values for its type;
That point has changed (DR1313); in n3485 it says:
- an operation that would have undefined behavior [ Note: including, for example, signed integer over- flow (Clause 5), certain pointer arithmetic (5.7), division by zero (5.6), or certain shift operations (5.8) — end note ];
[class.static.data]/3
If a non-volatile
const static
data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression
Conclusion: shifting SCHAR_MIN
is not a constant expression, so you can't do that in the in-class initializer of a static data member.
Hint: always compile with -Wall -Wextra -pedantic
. Using no parameters IMO is a bad idea for g++ and compatible compilers. g++/clang++ per default use the gnu99
mode (see clang doc), which is an extension to C++98 AFAIK. Also, you'll miss many important warnings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With