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

2.6k Views Asked by At

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

There are 2 best solutions below

0
On BEST ANSWER

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.

8
On

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
    {
        if constexpr(requires { t.foo(); })
        {
            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


However, if you find yourself using the same requires statement multiple times in your class, the original question why you just can't declare it once at class scope is justified.

Thus, in order to work around code duplication, you can declare a single static function which contains the concept you are looking for, e.g.

struct A
{
    template<typename T>
    static constexpr auto concept_foo(T t)
    {
        return requires{ t.foo(); };
    }

    template<typename T>
    auto operator()(T t) const
    {        
        if constexpr(concept_foo(t))
        {
            std::cout<<"foo"<<std::endl;
        }
        else
        {
            std::cout<<"no foo"<<std::endl;
        }
    }
};

This pattern could replace most use cases for concepts at class scope.