Barry's suggestion is one possibility, but if you have many things you want to test, you need to create many small files. What's more, those files could fail to compile for other reasons than what you expect, giving you a false sense your test passed.
An alternative is that instead of using static_assert
, you use some kind of SFINAE to detect whether or not something works. For traits classes this is a bit tricky, but you can do this:
template <class T>
using void_t = void;
template <class T>
struct foo;
template <>
struct foo <double> {};
template <class T, class = void>
struct has_foo_trait : std::false_type {};
template <class T>
struct has_foo_trait<T, void_t<decltype(foo<T>{})>> : std::true_type {};
int main(int, char**) {
std::cerr << has_foo_trait<int>::value;
std::cerr << has_foo_trait<double>::value;
return 0;
}
This prints out 01
. So now, instead of getting a hard failure from static_assert
ing directly, you can compute the value of the trait presence at compile time, and then static_assert
that you get the value you expect.
Note that the reason that traits classes are tricky is because the trait is declared as a general template, just not defined. So doing the "usual" metaprogramming thing of using the type directly inside the void_t
does not work. In order to trigger a soft SFINAE error in the true
branch of has_foo_trait
, I actually had to default construct an instance of the traits class. If you write your traits classes so they're not default constructible, this won't work. But in general you wouldn't write them that way. Would be curious to see if there's a better way to do it