3

I have converted one of my projects from VS 2019 to VS 2022 and the following conditional compilation template doesn't compile properly anymore:

struct T_USER;
struct T_SERVICE;

template<typename T>
class system_state
{
 public:
    system_state();
};


template<typename T>
system_state<T>::system_state()
{
    if constexpr (std::is_same<T, T_USER>)
    {
        std::cout << "User templ\n";
    }
    else if constexpr (std::is_same<T, T_SERVICE>)
    {
        std::cout << "Service templ\n";
    }
    else
    {
        //Bad type
        static_assert(false, "Bad template type in T: must be either T_USER or T_SERVICE");

        std::cout << "Unknown templ\n";
    }
}

The idea was to compile parts of code in system_state depending on a specific template, as such:

int main()
{
    system_state<T_USER> user_state;
}

But now the if constexpr std::is_same doesn't seem to detect my T and I'm always getting my static_assert clause:

Bad template type in T: must be either T_USER or T_SERVICE

What has changed? It used to work in VS 2019.

c00000fd
  • 20,994
  • 29
  • 177
  • 400

1 Answers1

1

The code is ill-formed because for constexpr if:

Note: the discarded statement can't be ill-formed for every possible specialization:

template <typename T>
void f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     else
       static_assert(false, "Must be arithmetic"); // ill-formed: invalid for every T
}

The common workaround for such a catch-all statement is a type-dependent expression that is always false:

template<class> inline constexpr bool dependent_false_v = false;
template <typename T>
void f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     else
       static_assert(dependent_false_v<T>, "Must be arithmetic"); // ok
}

You can use the type-dependent expression above in the else branch too, e.g.

//Bad type
static_assert(dependent_false_v<T>, "Bad template type in T: must be either T_USER or T_SERVICE");

BTW: In if constexpr (std::is_same<T, T_USER>), std::is_same<T, T_USER> is type but not a bool value; you should change it to std::is_same_v<T, T_USER>, or std::is_same<T, T_USER>::value, or std::is_same<T, T_USER>() (std::is_same<T, T_USER>{}).

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thanks. But what is `dependent_false_v`? – c00000fd Mar 16 '22 at 03:43
  • @c00000fd You can define it own as `template inline constexpr bool dependent_false_v = false;`. – songyuanyao Mar 16 '22 at 03:45
  • Yes, it works. Thanks. Can you explain what exactly does that dependent_false_v do and why did it use to work under VS 2019? – c00000fd Mar 16 '22 at 03:50
  • @c00000fd You have to make the condition for `static_assert` depending on template parameter `T`; you can't use `false` directly. I'm not sure about VS2019, it might be a bug fixed from VS2022. – songyuanyao Mar 16 '22 at 03:55
  • 2
    @songyuanyao It is a conformance improvement: implemented in VS 2022 https://learn.microsoft.com/en-us/cpp/overview/cpp-conformance-improvements?view=msvc-170 It says This change is a source breaking change. It applies in any mode that implies /Zc:permissive- or /Zc:static_assert. This change in behavior can be disabled by using the /Zc:static_assert- compiler option. – Jerry Jeremiah Mar 16 '22 at 04:11