if constexpr and dependent false static_assert is ill formed?
Asked Answered
T

1

4

A related question provides the example of a type-independent false in a static_assert:

template<class T> void foo()
{
    if constexpr(false)
        static_assert(false);
}

However, I am more concerned if the same thing applies to a type-dependent false. Here is the relevant quote from the standard:

The program is ill-formed, no diagnostic required, if no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated. § 13.7/8.1

This comes as a surprise to me as I frequently see the following idiom:

template<class T> void foo()
{
    if constexpr(cond)
        // ...
    else
        static_assert(!std::is_same_v<T, T>);
}

In fact, cppreference even provides an example of the same thing:

template<class T> struct dependent_false : std::false_type {};
template<class T> void foo()
{
    if constexpr (cond)
        // ...
    else
        static_assert(dependent_false<T>::value);
}

From my understanding, in both of these cases, no valid specialization can ever be generated for the relevant if constexpr substatements, and are therefore ill-formed, no diagnostic required. Am I correct?

Thinner answered 5/9, 2019 at 19:40 Comment(1)
Very related: #57788166Myosotis
M
5

no valid specialization can ever be generated for the relevant if constexpr substatements

Well no.

You could have a valid specialization of that branch if you specialized dependent_false first:

template <> struct dependent_false<int> : std::true_type {};
///
foo<int>(); // <-- A valid specialization for the `else` branch is possible here

The fact that it's possible to create such a specialization (even if you don't have one) makes static_assert(dependent_false<T>::value); well-formed.

On the other hand, static_assert(!std::is_same_v<T, T>); is ill-formed NDR, because specializing templates from std:: is not allowed.

Myosotis answered 5/9, 2019 at 19:48 Comment(10)
Doesn't this mean that the cppreference example is actually bad? Because there is no specialization for int there. Furthermore, we want to have false every type, int should not be an exception. So a better type is the one that Jarod42 used, decltype([](){}).Dingess
@Dingess I'm not saying you need to create a specialization of dependent_false to make the code well-formed. I'm saying it's well-formed because you could create a specialization.Myosotis
@Dingess Edited for clarity.Myosotis
Hmm. Thinking about this again, isn't it the case, that if cond can ever be true, then we're good? There is no need to have the possibility that static_assert's condition can ever be true, because if cond can be true, there is already a valid specialization.Dingess
On the other hand, if cond always false, then we must have a dependent_false specialization, where it gives true, because if we didn't have one, all the specializations are invalid. So in this case, it is not enough, that in theory, we could add one, but we need to add one.Dingess
@Dingess The standard requires that a valid specialization has to exist for each if constexpr branch, not only for the entire template.Myosotis
I didn't know that, thanks for the info. Then, isn't it the case, that we must add a true specialization for dependent_false? It is not enough, that we could add, but we must do it? (If we don't add one, then the program is ill-formed, because there is no valid specialization exists for the static_assert branch)Dingess
@Dingess I think we don't have to, but I'm not completely sure. Maybe we should make a new question about it.Myosotis
Isn't the gist of this question about this? :)Dingess
@Dingess Oh well. :)Myosotis

© 2022 - 2024 — McMap. All rights reserved.