问题
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