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?

seaplusplus
  • 153
  • 3
  • Very related: https://stackoverflow.com/questions/57787666/is-it-possible-to-implement-always-false-in-the-c-standard-library – HolyBlackCat Sep 05 '19 at 19:48

1 Answers1

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.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 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([](){})`. – geza Sep 06 '19 at 06:36
  • @geza 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. – HolyBlackCat Sep 06 '19 at 06:39
  • @geza Edited for clarity. – HolyBlackCat Sep 06 '19 at 06:47
  • 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. – geza Sep 06 '19 at 06:47
  • 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. – geza Sep 06 '19 at 06:51
  • @geza The standard requires that a valid specialization has to exist for each `if constexpr` branch, not only for the entire template. – HolyBlackCat Sep 06 '19 at 06:52
  • 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) – geza Sep 06 '19 at 06:54
  • @geza I think we don't have to, but I'm not completely sure. Maybe we should make a new question about it. – HolyBlackCat Sep 06 '19 at 06:57
  • Isn't the gist of this question about this? :) – geza Sep 06 '19 at 07:01
  • @geza Oh well. :) – HolyBlackCat Sep 06 '19 at 07:04