6
template<typename T, typename U = void>
struct S { /* static_assert(0, "type unsupported"); */ };
template<typename T>
struct S<T, typename std::enable_if<std::is_integral<T>::value, void>::type> {
    void foo() {}
};
...
S<int> i;
i.foo();
S<double> d;
// d.foo();

I would be expecting that the "master template" would never be instantiated for the case of int, but if I uncomment the static_assert, the S<int> instantiation will fail. Even a lone typedef S<int> Si; would fail to compile. (GCC 4.9.2 Cygwin)

What I aimed to achieve is not for S<double> to fail at the foo() call, but at the instantiation of the template itself, and that with a meaningful error message. I'm aware I can do something like typename T::nonexistent_type t; in the master template which will prevent the template for compiling, but that'd be inferior to a static_assert message. (Note: putting the static_assert within a function definition in the master template still fails compilation for S<int>)

Why does the static_assert fail even though that template is not instantiated? Is this mandated (or perhaps "unspecified") by the standard? Is there a way to fail with a static_assert the way I wish to?

Irfy
  • 9,323
  • 1
  • 45
  • 67

1 Answers1

7

The expression in the static_assert must be dependent on a template parameter if you wish it to be instantiation-time only. This is guaranteed by Standard- the implementation may (but has no obligation to) check static_assertions in templates that are not dependent on any template parameter.

Your code is a strange roundabout way of doing something like

template<typename T> struct S {
    static_assert(std::is_integral<T>::value, "type unsupported");
    void foo() {}
};

This clearly communicates to the compiler the dependency between the expression and the template parameter, and is far clearer and easier to read as well. I actually couldn't quite figure out if you wanted compilation to fail for integral types or for non-integral types.

JFMR
  • 23,265
  • 4
  • 52
  • 76
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • I've reduced my original problem to the simplest one for demonstrating the problem. I need to discern various cases at compile time and fail for anything else, not matched by the many partial specializations. Thus the code above. I just realized that *any* dependency on the type suffices: `static_assert::value && 0, "type unsupported">` does what I want. (almost -- `typedef S Sd` still compiles, but `Sd d` declaration fails, so it's rather okay) – Irfy May 17 '15 at 13:02
  • @lrfy: Then the simplest thing to do is make an `is_valid` trait that combines the various partial specialization cases and use that as the conditional – Puppy May 17 '15 at 14:06
  • @Puppy - I just had something similar myself. Your `is_valid` solution sometimes means writing two templates, both with all the specializations, instead of just one - and purely to get a better error message from the one you actually want. That could be a fair bit of clutter, and duplicating all the specialization cases means they could easily be inconsistent (though AFAICT that should never lead to run-time bugs). Writing the `static_assert` condition weirdly could be a better approach, and possibly it would be worthwhile having a `dependent_false` template. –  Sep 22 '15 at 04:41