The example is problematic, in that:
1. click() should not be a member of the widget. A widget does not click; a user clicks a widget. A click can change a widget's state, but the state might change because of other effects, e.g. pressing a key when the widget is focused. But then, that's just one of the issues with treating UI widgets this way.
2. More to the point - clickCount. If this is a button, it shouldn't keep a record, or aggregate, of its clicks within it; and if it's a widget where this does really matter, like a range control where more clicks mean a value that goes farther along the range - you still would not keep the count of clicks, but the current position. Statistics about the interaction with an object should not be part of the object itself. At most it might be legitimate to have, say, a Widget class, a template like <class Stats> StatisticsTracker , and then class TrackedWidget which uses that as a mixin, i.e. inheriting both Widget and StatisticsTracker<ClickStats>. And that's already stretching it beyond what I would find reasonable.
3. Having something named is another aspect of objects which may be a good fit for a mixin class.
Anyway, an 'indirect' type for objects you don't know the definition of sounds nice.
A few more nitpickis about the example:
1. Instead of explicitly applying the rule-of-0 with `= default` for the copy&move ctor&assignment and the destructor - just _don't_ write anything:
class Widget
{
public:
void click();
int clickCount() const;
std::string label() const;
private:
struct Impl;
std::indirect<Impl> pimpl_;
};
and that's the beauty of the rule of 0.2. Why return an std::string for the label? The label() method should return an std::string_view
> 2. Why return an std::string for the label? The label() method should return an std::string_view
This only works if it's always the same value. This doesn't work if the label is for example, set to `std::to_string(clickCount())`
This is just a simple example, therefore nitpicking on the semantics of the Widget methods is a bit silly.
> 1. Instead of explicitly applying the rule-of-0 with `= default` for the copy&move ctor&assignment and the destructor - just _don't_ write anything:
The blog post explicitly explains why this doesn't work. You have to define these methods in the source file because they need to see the definition of the Impl struct.