C++20s concepts with a forward declared type
In today's post, I would like to continue talking about forward declared or better incomplete types in C++.
I discussed some scenarios in last months post Forward declaring a type in C++: The good, and the bad. Today I like to add another flavor, C++20s concepts.
It depends
It's time to tune up the heat. Let's use C++20's concepts and play with the cat from last month:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
This example starts as the initial one in this article by forward declaring the class Cat. Using C++20, I create a concept CanPlay in B checking that a type T has a member function Play. I use that concept inside Play in C. That way, Play prints Playing with pets is great! if the member function exists and otherwise Sadly, no pet to play with!.
The function Play is then used inside Fun, which itself takes Cat reference D.
Only after that, I add the definition of Cat, of course, with the member function Play E. Finally, in F, I create a Cat object and call Fun. All clear? Good!
Same question as in my last post: what do you think is the output of this program? The answer is just one down-scroll away.
Obviously:
1 | |
That was an easy one, right? Sorry, I didn't want to disappoint you. How about now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
You're looking for the change? Sorry, I made Play return auto. For sure nothing to worry about.
The output now? Well, please take your time before scrolling down to see the answer.
1 | |
Wow, why did the output change? Because of the auto return type. This forces the compiler to eagerly instantiate Play by looking into the function's body for the return type. Without auto the compilers delay the instantiation to the end of the translation unit. This isn't possible with auto return type. But the eager instantiation in Fun happens now at a time when Cat is still an incomplete type. That is no issue for the concept, it can work with incomplete types. However, the concept only has the knowledge of Cat present at this time. And that is, there is a type Cat without a member function Play. My post Efficient C++: The hidden compile-time cost of auto return types covers the implications a bit more.
There are other scenarios where the result of your concept can change when being used with an incomplete type.
Summary
I can only repeat last months summary: Using incomplete types to forward declare classes is a great way to reduce compile times. However, it also opens the door for undefined or at least surprising behavior. Be extra careful when forward-declaring types.
Andreas
