c++c++20c++-concepts

Can requires expression return TRUE for ill-formed code?


The following code compiles. Is it expected? If so, why? What does requires check in that case?

template <int I> void f() { static_assert(I); }

int main() {
    static_assert(requires{f<0>();});
}

example


Solution

  • C++ has this concept called the immediate context.

    Failures in the immediate context are checkable, those outside are not. SFINAE (Substitution Failure Is Not An Error) really only applies to those failures in the immediate context. As part of substitution.

    Immediate context isn't very precisely defined at the moment, but it basically refers to things that are available in the signature of the function or function template - not in its body. Once you get to the body, it's game over. It's either valid or a hard failure.

    So here:

    template <int I> void f() { static_assert(I); }
    
    static_assert(requires { f<0>(); });
    

    We are checking that the expression f<0>() is valid. That's only an immediate context check that does not care about the body. Which is to say, we're just looking at this:

    template <int I> void f();
    
    static_assert(requires { f<0>(); });
    

    That's perfectly valid, so the assertion passes.

    But there are cases where we have to look at the body. Imagine instead f returned auto instead of void:

    template <int I> auto f2() { static_assert(I); }
    //               ^~~~
    
    static_assert(!requires { f2<0>(); });
    

    Now, in order to ascertain the validity of f2<0>(), we have to actually determine the return type, which requires instantiating the body. That will hit the static_assert. But now we're outside of the immediate context, so it's a hard failure rather than a checkable one. Even though I'm trying to assert that the expression is invalid, it still fails.