There seems to be a broad lack of understanding of what this feature
is doing [...] The goal of the proposal is to be able to write a function that takes
a single pointer to any type which inherits from *two or more* base
classes. And then use that pointer to access any function which is
available from any of these base classes. [...]
Personally, I don't think this feature is worth doing. The
concept/template solution is adequate for those who need it, and it's
probably not something that is *broadly* useful. It's a niche feature.
Ditto, but my reason for not-adding-this-feature-to-C++ wouldn't be "it's niche" so much as "you don't need a core-language feature for this; you can already accomplish your goal just by using the existing language."
Consider again the example [slightly simplified]:
struct Control { virtual void SetBackgroundColor(); };
struct TextEntry { virtual void SetValue(const char*); };
struct TextCtrl1 : Control, TextEntry {};
struct TextCtrl2 : Control, TextEntry {};
void Red(??? *p) {
p->SetBackgroundColor();
p->SetValue("pending");
}
int main() {
TextCtrl1 tc1;
Red(&tc1);
Red(&tc2);
}
Frederick's programming problem is: How do we write function `Red` so that it can accept either TextCtrl1 or TextCtrl2, and yet without making `Red` itself a function template? (We don't want `Red` to be a template because that would force its whole definition to be defined in a header file, which means recursively including all the stuff used in that definition, and so on.)
Frederick's suggested solution was to just make it work by magic:
void Red(__chimerical(Control, TextEntry) *p) {
p->SetBackgroundColor(); // this means Control::SetBackgroundColor
p->SetValue("pending"); // this means TextEntry::SetValue
}
However, this has problems. What would happen if the programmer wrote this code, and then later the maintainer of wxWidgets changed the definition of `Control` as follows?
struct Control { void SetBackgroundColor(); void SetValue(const char*); };
Now:
p->SetValue("pending"); // Does this still mean TextEntry::SetValue? if so, why? Is it now ambiguous and ill-formed?
This is related to the guideline "Don't inherit from types you don't control." This magic __chimerical(X,Y) becomes a method of "ad-hoc inheritance," by which any client programmer can introduce an inheritance-like dependency coupling the interfaces of any two classes X and Y.
If you were proposing to implement the magic behavior by changing name lookup, then you also have to explain whether
struct Control { void SetBackgroundColor(); void SetValue(); };
would be treated similarly: Is `p->SetValue("pending")` still ambiguous, or do we do overload resolution among all the possible meanings of `SetValue` and pick the best match, or what?
Anyway, you don't need to change the language for this. There are well-established programming techniques to accomplish your goal.
The simplest would be to completely open-code it:
void Red(void *p) {
struct Dummy { virtual ~Dummy(); };
auto asControl = [](void *p) {
return dynamic_cast<Control*>(static_cast<Dummy*>(p));
};
auto asTextEntry = [](void *p) {
return dynamic_cast<TextEntry*>(static_cast<Dummy*>(p));
};
asControl(p)->SetBackgroundColor();
asTextEntry(p)->SetValue("pending");
}
This `Red` accepts a pointer to anything inheriting from both Control and TextEntry. Of course it also accepts a pointer to anything else (e.g. an `int`), and has UB if you do that; but, don't do that. You can check for that by tossing a simple template on the front end:
template<class T>
void Red(T *p) {
static_assert(is_base_of_v<Control, T> && is_base_of_v<TextEntry, T>);
RedImpl(p); // calls RedImpl(void*) as above
}
So that's the first option that exists today. The second option is to introduce your own "TextCtrl" interface, either as a base class (if you control the hierarchy at some point above `TextCtrl1` and `TextCtrl2`):
struct TextCtrl : Control, TextEntry {};
struct TextCtrl1 : TextCtrl {}; // CHANGED!!
struct TextCtrl2 : TextCtrl {}; // CHANGED!!
void Red(TextCtrl *p) {
p->SetBackgroundColor();
p->SetValue("pending");
}
or using type erasure (if you don't control the hierarchy):
struct TextCtrlPtr {
TextEntry *t_;
Control *c_;
template<class T> TextCtrlPtr(T *p) : t_(p), c_(p) {}
TextEntry *asTextEntry() const { return t_; }
Control *asControl() const { return c_; }
};
void Red(TextCtrlPtr p) {
p.asControl()->SetBackgroundColor();
p.asTextEntry()->SetValue("pending");
}
Obviously you can polish `TextCtrlPtr` by making `SetBackgroundColor` and `SetValue` into member functions of it, if you really want to.
For certain heavy-OOP-design-pattern-ish applications you might even want to introduce an adaptor class of your own. Here `TextCtrl1` and `TextCtrl2` are out of our control, but we can write a base class we want to use (`TextCtrl`) and then a concrete adaptor class `TextCtrlAdaptor` that can wrap a pointer to any type that implements both original base classes. This is the same as the type-erasure approach, except that the inheritance is explicit, and the `TextCtrlAdaptor` object actually IS-A `TextCtrl`. This is the first implementation where the `virtual` keyword really matters to the correctness of the example.
struct TextCtrl1 : Control, TextEntry {};
struct TextCtrl2 : Control, TextEntry {};
struct TextCtrl : Control, TextEntry {};
struct TextCtrlAdaptor : TextCtrl {
TextEntry *t_;
Control *c_;
template<class T> explicit TextCtrlAdaptor(T *p) : t_(p), c_(p) {}
void SetBackgroundColor() override { c_->SetBackgroundColor(); } // overrides virtual Control::SetBackgroundColor
void SetValue(const char *s) override { t_->SetValue(s); } // overrides virtual TextEntry::SetValue
};
void Red(TextCtrl *p) {
p->SetBackgroundColor();
p->SetValue("pending");
}
int main() {
TextCtrl1 tc1;
TextCtrl2 tc2;
TextCtrlAdaptor ta1(&tc1); Red(&ta1);
TextCtrlAdaptor ta2(&tc2); Red(&ta2);
}
HTH,
Arthur