In C++, static_assert
is a compile-time assertion that verifies whether a condition is true at compile time. If the condition is false, the compiler will issue an error message and stop the compilation process.
When using static_assert
in a templated member function of a non-template class, you may encounter an error if the assertion condition is false
and the function is never instantiated. This is because some compilers have not implemented a solution for DR2518, which states that:
- If the value of the expression when so converted is
true
or the expression is evaluated in the context of a template definition, the declaration has no effect. Otherwise, thestatic_assert
-declaration fails
Since you have static_assert(false, "...");
in a template definition that is uninstantiated, it should not be refused by the compiler.
Implementations which haven't implemented a solution for the above defect report still abide by the old rules, shown in the old part of this answer:
In a
static_assert
-declaration, the constant-expression is contextually converted tobool
and the converted expression shall be a constant expression ([expr.const]
). If the value of the expression when so converted istrue
, the declaration has no effect. Otherwise, the program is ill-formed, and the resulting diagnostic message ([intro.compliance]
) should include the text of the string-literal, if one is supplied.
By putting false
in your static_assert
you've made the program ill-formed and the compiler is correct when rejecting the program.
A possible workaround could be to make the assertion dependent on the template parameter, at least superficially.
template<class> struct always_false : std::false_type {}; template<class T> inline constexpr bool always_false_v = always_false<T>::value; class TestClass { size_t MemberFn() { /* shared stuff between trivial and non-trivial */ return 0; } template <typename Type> size_t MemberFn( std::enable_if_t<!std::is_trivially_constructible_v<Type> || !std::is_trivially_destructible_v<Type>>* = nullptr) { static_assert(always_false_v<Type>, "not implemented yet"); return 0; } template <typename Type> size_t MemberFn( std::enable_if_t<std::is_trivially_constructible_v<Type> && std::is_trivially_destructible_v<Type>>* = nullptr) { static_assert(always_false_v<Type>, "not implemented yet"); return 0; } };
Another answer:
Raymond Chen covered this issue in his blog:
How can I create a type-dependent expression that is always false?
auto lambda = [total](auto op, auto value) mutable { ... static_assert(false, "Don't know what you are asking me to do."); ... };
However, this does not compile because the
static_assert
fails immediately.The reason is that the controlling expression for the
static_assert
is not dependent upon the type of the arguments, and therefore it is evaluated when the lambda is compiled, not when the lambda is invoked (and the implicit template instantiated).In order to defer the
static_assert
to instantiation, we need to make it type-dependent.
Of what use is a type-dependent expression that is always false?
Last time, we saw how to create a type-dependent expression that is always false, and used it in a potentially-discarded statement so that the assertion failed only if the statement ended up being used.
Another case where you want to defer a static assertion failure to instantiation is if you want to reject a particular specialization.
So, you need to make the static_assert
condition be dependent on the template argument. Raymond showed two ways to do that:
Using a custom helper template:
#include <type_traits>
template<typename>
inline constexpr bool always_false_v = false;
class TestClass
{
...
template<typename Type>
size_t MemberFn(std::enable_if_t<!std::is_trivially_constructible_v<Type> || !std::is_trivially_destructible_v<Type>>* = nullptr)
{
static_assert(always_false_v<Type>, "not implemented yet");
return 0;
}
template<typename Type>
size_t MemberFn(std::enable_if_t<std::is_trivially_constructible_v<Type> && std::is_trivially_destructible_v<Type>>* = nullptr)
{
static_assert(always_false_v<Type>, "not implemented yet");
return 0;
}
};
Using sizeof()
:
#include <type_traits>
class TestClass
{
...
template<typename Type>
size_t MemberFn(std::enable_if_t<!std::is_trivially_constructible_v<Type> || !std::is_trivially_destructible_v<Type>>* = nullptr)
{
static_assert(!sizeof(Type*), "not implemented yet");
return 0;
}
template<typename Type>
size_t MemberFn(std::enable_if_t<std::is_trivially_constructible_v<Type> && std::is_trivially_destructible_v<Type>>* = nullptr)
{
static_assert(!sizeof(Type*), "not implemented yet");
return 0;
}
};
```