问题
I have a class template which can't be used directly, only specializations are allowed. And I want to use static_assert to show meaningful error message.
I can't just type static_assert(false, "error"); since false isn't value dependent and compiler may show error message even if the template is never used.
My solution:
template<class>
struct AlwaysFalse : std::false_type{};
#define DEPENDENT_FALSE(arg) AlwaysFalse<decltype(sizeof(arg))>::value
template<class T>
struct Foo{
static_assert(DEPENDENT_FALSE(T), "You must use specialization!");
};
template<int i>
struct Bar{
static_assert(DEPENDENT_FALSE(i), "You must use specialization!");
};
But I'm not sure about realization DEPENDENT_FALSE. Because MSVC doesn't treat sizeof(arg) as template dependent expression(unlike GCC), but decltype(sizeof(arg)) is fine.
Can somebody explain this behavior in terms of standard? Is it portable?
回答1:
This:
#define DEPENDENT_FALSE(arg) AlwaysFalse<decltype(sizeof(arg))>::value
fails to actually be dependent. decltype(sizeof(arg)) is always size_t, it doesn't actually depend on arg in any way (more broadly, here is a list of expressions that are never type-dependent). Since it's not dependent, a compiler is perfectly able to see that DEPENDENT_FALSE(T) is false and just trigger that static_assert.
What you want is just:
#define DEPENDENT_FALSE(arg) AlwaysFalse<decltype(arg)>::value
That is, drop the sizeof. This now is dependent.
This won't work for the int directly, since that again won't be dependent (decltype(i) is just int, and we need something value-dependent now). For that, you can just wrap it in an integral constant:
template<class T>
struct Foo{
static_assert(AlwaysFalse<T>::value, "You must use specialization!");
};
template<int i>
struct Bar{
static_assert(AlwaysFalse<std::integral_constant<int, i>>::value, "You must use specialization!");
};
回答2:
Maybe the following idea:
template<typename T>
struct AlwaysError
{
AlwaysError<T>()
{
std::cout << T::value << std::endl;
static_assert( T::value, "You must use specialization!");
}
};
template <typename T>
struct Foo: public AlwaysError<Foo<T>>
{
static constexpr bool value = false;
};
template <>
struct Foo<int>{};
int main()
{
//Foo<double> d; // error message as expected
Foo<int> i; // specialized, compiles!
}
The assert message contains the text and also the type which should be specialized. In hope it helps!
回答3:
Solution which works on C++17. Unfortunately I have only C++11
template<auto>
constexpr bool dependentFalseHelper(){
return false;
}
template<class>
constexpr bool dependentFalseHelper(){
return false;
}
#define DEPENDENT_FALSE(arg) (dependentFalseHelper<arg>())
来源:https://stackoverflow.com/questions/51523965/template-dependent-false