Can this incomplete type be improved to work with this C++ concept?

192 views Asked by At

The following code works as designed with g++ 9.3.1 and the old concepts TS. But I haven't gotten it to work with g++ 10.3.1 and the C++ core language version of concepts:

#if __cpp_concepts < 201707
#  define CONCEPT concept bool
#else
#  define CONCEPT concept
#endif

template<class T>
CONCEPT AcceptsEvents = requires (T t) {
  t.OnEvent();
};

template <AcceptsEvents T>
struct Inner { };

struct Outer {
  void OnEvent();
  Inner<Outer> inner;
};

int main() {
  Outer out;
}

Here's my g++ 9.3.1 compilation with concepts TS:

$ g++ -std=c++2a -fconcepts concepts.cpp

It builds with no errors.

Here's my g++ 10.3.1 compilation with the C++ concepts core language feature:

$ g++ -std=c++2a -fconcepts-diagnostics-depth=2 concepts.cpp

This fails to compile with the following paraphrased error message:

note: constraints not satisfied
required for the satisfaction of 'AcceptsEvents<T>' [with T = Outer]
 in requirements with 'T t' [with T = Outer]
note: the required expression 't.OnEvent()' is invalid, because
error: 't' has incomplete type

I've read both the concepts TS and core language concepts pages at cppreference.com in detail, and I've read this SO answer in an effort to coax class Outer into a complete type. Any suggestions for design improvements to get the 10.3.1 version working like the old 9.3.1 version was?

2

There are 2 answers

1
bolov On

You can separate the event logic. Depending on your data structures this could make sense or not.

Inheritance

struct Event
{
    void OnEvent();
};

struct Outer : Event {
  Inner<Event> inner;
};

Composition

struct Event
{
    void OnEvent();
};

struct Outer {
  Event evt;
  Inner<Event> inner;
};
0
Yakk - Adam Nevraumont On
auto OnEvent(auto& t)->decltype(t.OnEvent()){
  return t.OnEvent();
}

template<class T>
concept AcceptsEvents = requires (T t) {
  OnEvent(t);
};

template <AcceptsEvents T>
struct Inner { };

struct Outer;
void OnEvent(Outer&);

struct Outer {
  void OnEvent();
  Inner<Outer> inner;
};

void OnEvent(Outer&o){o.OnEvent();}

that should work. Not tested, little practical experience with concepts, so it could be wrong; but seems right to my head-compiler.

I changed the concept from requireing a method to a function call. Then added a trampoline template that calls the method. So it checks both; and existing types (when complete) should work.

This lets us define a free function on an incomplete Outer object, so Outer passes the test.

Throw in some namespaces and we can even call the trampoline.