Why can't I declare a concept at class scope?

Viewed 1450

Consider this code:

struct A
{
    template <typename T>
    concept foo = true;
};

It doesn't compile. My Clang 10 gives me error: concept declarations may only appear in global or namespace scope, and GCC says something similar.

Is there a reason why it's not allowed? I don't see why it couldn't work, even if the enclosing class was a template.

2 Answers

The fundamental difficulty that would arise is that concepts could become dependent:

template<class T>
struct A {
  template<T::Q X>
  void f();
};

Is X a non-type template parameter of (dependent) type T::Q (which does not require typename in C++20), or is it a type template parameter constrained by the concept T::Q?

The rule is that it’s the former; we would need new syntax (along the lines of typename/template) to express the other possibility: perhaps something like

template<T::concept Q X> requires T::concept R<X*>
void A::g() {}

No one has explored such an extension seriously, and it could easily conflict with other extensions to concept syntax that might be more valuable.

The why is discussed in the answer by @DavisHerring. In this answer I want to share a pattern I was actually looking for when I encountered this question.

Depending on your use case, you maybe don't need the concept definition. If all you want to do is to avoid SFINAE trickery, you can directly invoke the requires clause and get rid of any concept at class scope:

struct A
{
    template<typename T>
    auto operator()(T t) const
    {
        constexpr auto has_foo = []<typename U>(U)
        {
            return requires(U)
            {
                 std::declval<U>().foo();
            };
        };
        
        if constexpr(has_foo(t))
        {
            std::cout<<"foo"<<std::endl;
        }
        else
        {
            std::cout<<"no foo"<<std::endl;
        }
    }
};

and use that as

struct B { auto foo() {} };
struct C {};

int main()
{
    A a;
    a(B());  //prints "foo"
    a(C());  //prints "no foo"
}

DEMO

Related