I finally switched to MSVC 2022 in the last couple days and am getting a static_assert from code that had previously been working fine.
I have a type that needs to have a member implemented differently based on whether a template parameter type is trivally constructable and destructable or not, but have not yet actually implemented any of that logic. I've been using static_assert(false, "not yet implemented") as a guard against accidental use of the member.
I've pared it down to the following example:
#include <type_traits>
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(false, "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(false, "not implemented yet");
return 0;
}
};
When I try building this I get the following (and similar for the second member template):
2>D:\projects\TestLib\TestLib\testlib.h(18,17): error C2338: static_assert failed: 'not implemented yet'
2>D:\projects\TestLib\TestLib\testlib.h(16,9): message : This diagnostic occurred in the compiler generated function 'size_t TestClass::MemberFn(enable_if<!std::is_trivially_constructible_v<Type,>||!std::is_trivially_destructible_v<Type>,void>::type *)'
Note that I do not actually have a call to this function anywhere, and the diagnostic does not tell me what actual type the compiler is trying to use. Basically I wish to go back to this particular function being ignored as it did with MSVC 2019.
I am compiling with /std:c++latest and /permissive- and would prefer to keep those.
What am I missing here?
This is because your compiler has not implemented a solution for DR2518 which states that
true
or the expression is evaluated in the context of a template definition, the declaration has no effect. Otherwise, the static_assert
-declaration failsSince 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;
}
};