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?
CodePudding user response:
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;
};
CodePudding user response:
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.