3

I've noticed strange behavior of static_assert:

#include <iostream>

template <typename T, unsigned int D> struct Vec
{
    static_assert(D && 0, "Invalid dimension for vector!");
};

template <typename T> struct Vec<T, 1>             {union {T x, r;};};
template <typename T> struct Vec<T, 2> : Vec<T, 1> {union {T y, g;};};
template <typename T> struct Vec<T, 3> : Vec<T, 2> {union {T z, b;};};
template <typename T> struct Vec<T, 4> : Vec<T, 3> {union {T w, a;};};

int main()
{
    Vec<float, 3> v;
    v.x = 1;
    v.y = 2;
    v.z = 3;

    return 0;
}

It compiles fine: http://ideone.com/wHbJYP . I would expect

static_assert(0, "Invalid dimension for vector!");

to give me same result, but it causes static assertion failure: http://ideone.com/UEu9Kv . Is gcc correct in both cases? If so, why? Or is it a gcc bug? Then, in which case gcc is correct?

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207

2 Answers2

7

§14.6 [temp.res]/p8:

If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required.

In both cases no valid specialization can be generated for the primary template due to the static_assert (D && 0 is never true no matter the value of D). Since no diagnostic is required, the compiler is free to diagnose one (when you use 0) but not the other (when you use D && 0).

Workaround:

template <unsigned int D> struct always_false : std::false_type {};

template <typename T, unsigned int D> struct Vec
{
    static_assert(always_false<D>::value, "Invalid dimension for vector!");
};

The compiler can no longer reject this at definition time, as there might be an explicit specialization of always_false whose value member is true.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • The weird part is that by that explanation the first static assert (D && 0) should also cause the template to be ill formed but that one compiles fine. I'm curious, too, as to what's going on here. – mbgda Jan 09 '15 at 19:51
  • @mbgda "ill-formed" only requires the compiler to produce a diagnostic message (which can be an error or a warning). "no diagnostic required" means that the compiler is allowed to compile the code even without producing a diagnostic. – T.C. Jan 09 '15 at 19:51
  • @TC I thought you said no diagnostic is required only if the template is not instantiated - did I read it incorrectly? – mbgda Jan 09 '15 at 19:54
  • @mbgda The OP didn't instantiate the primary template. The code instantiated only the partial specializations. – T.C. Jan 09 '15 at 19:55
  • @T.C. Looks like your explanation is not fully correct. `std::is_void::value` condition doesn't work, but `std::is_void::value` is ok. It's strange, because I can redefine them both to be `1`. Do you have any ideas? – HolyBlackCat Jan 09 '15 at 22:05
  • 1
    @HolyBlackCat `std::is_void::value` doesn't work because nothing in there is dependent on a template parameter, so everything is looked up and evaluated at template definition time. Also, specializing a standard library type trait (except `std::common_type` for user-defined types) is undefined behavior. – T.C. Jan 09 '15 at 22:43
  • @T.C. Ok, thanks. BTW, this post (http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228797#228797) says that template specializations are allowed, that's why I assumed that I can do it. – HolyBlackCat Jan 09 '15 at 23:05
  • @HolyBlackCat Most of them are allowed for user-defined types, but type traits are special. – T.C. Jan 09 '15 at 23:16
0

Everything is not 1,2,3,4 (for which specific specializations exist) will go into the "master" definition, that have to assert for every value of D it will be called.

So you need an expression that contains D and that is always false, to cause the compiler to evaluate it in dependency of D

If you just use 0, it will be anymore dependent and the compiler will evaluate as it matches during parsing, causing the assertion to always take place. Even if it will not be the class you will instantiate.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63