

Resolving the ambiguity when your C++ class inherits from multiple base classes...
source link: https://devblogs.microsoft.com/oldnewthing/20210813-05/?p=105554
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Resolving the ambiguity when your C++ class inherits from multiple base classes that have the same method
Raymond
August 13th, 2021
Suppose you have a class which derives from two base classes, both of which have the same method.
struct Base1 { void Something(); bool IsReady(); }; struct Base2 { void Something(); bool IsReady(); }; struct Derived : Base1, Base2 { }; void oops() { Derived d; d.Something(); // ambiguous call to Something() }
What happens next depends on what you want.
If you want the derived class to call both base class methods, then you get to write that out, specifying the order in which you want the calls to occur and how you want the results to be combined:
struct Derived : Base1, Base2 { void Something() { Base1::Something(); Base2::Something(); } bool IsReady() { return Base1::IsReady() && Base2::IsReady(); } };
On the other hand, maybe you want everything to go to Base1
:
struct Derived : Base1, Base2 { void Something() { Base1::Something(); } bool IsReady() { return Base1::IsReady(); } };
Writing out this forwarder can get cumbersome if you have lots of methods, or if the methods have parameters that you need to perfect-forward:
struct Derived : Base1, Base2 { template<typename... Args> void Something(Args&&... args) { Base1::Something(std::forward<Args>(args)...); } template<typename... Args> bool IsReady(Args&&... args) { return Base1::IsReady(std::forward<Args>(args)...); } };
Fortunately, there’s a trick: You can steer the compiler toward the implementation in one of the base classes by using a using
declaration.
struct Derived : Base1, Base2 { using Base1::Something; using Base1::IsReady; };
One use of the using
declaration is to import a method from a base class so you can add overloads to it. (If you don’t import the methods from the base class, then your overloads shadow the base class methods.) Here, we are importing a method from a base class and not adding any overloads. We just want the method to be treated as if it were directly in our class all along.
This trick comes in handy in C++/WinRT stateful factories: The standard mechanism for implementing a stateful factory is to override the stateless factory methods provided by the WidgetT
template with your own stateful ones.
namespace winrt::factory_implementation::Widget { struct Widget : WidgetT<Widget, implementation::Widget, static_lifetime> { void Method(); // stateful static }; }
Windows Runtime static methods can be an aggregation of multiple independent providers that march together under one flag. Factoring out the functionality into separate classes is helpful if the class has a large number of static methods which fall naturally into categories of related methods. For example, there might be some static Widget members that use Plug and Play (a static WidgetsChanged
event and a static FindAllWidgets
method), and another group of static Widget methods that deal with system configuration (SetMaximumActiveTime
, AllowDownloadedContent
).
The naïve version doesn’t work:
namespace winrt::factory_implementation::Widget { struct WidgetPlugAndPlay { event_token WidgetsChanged(TypedEventHandler<IInspectable, IInspectable> handler); void WidgetsChanged(event_token token); IVectorView<Widget> FindAllWidgets(); }; struct WidgetConfiguration { void SetMaximumActiveTime(TimeSpan limit); bool AllowDownloadedContent(); void AllowDownloadedContent(bool value); }; struct Widget : WidgetT<Widget, implementation::Widget, static_lifetime>, WidgetPlugAndPlay, WidgetConfiguration { }; }
It doesn’t work because the static methods are multiply-inherited: Once from the WidgetT
template (which forwards to static methods on the implementation class) and once from the WidgetPlugAndPlay
and WidgetConfiguration
base classes. To resolve the ambiguity, you can steer the methods with a using
declaration.
namespace winrt::factory_implementation::Widget { struct Widget : WidgetT<Widget, implementation::Widget, static_lifetime>, WidgetPlugAndPlay, WidgetConfiguration { using WidgetPlugAndPlay::WidgetsChanged; using WidgetPlugAndPlay::FindAllWidgets; using WidgetConfiguration::SetMaximumActiveTime; using WidgetConfiguration::AllowDownloadedContent; }; }
Related: How can I write a C++ class that iterates over its base classes?
Recommend
-
11
February 22, 2020 | Edit Resolving Ambiguity in Text Rewriting...
-
17
I’ve been an interview coach for a long time now, and in the past I didn’t hear about many interview questions dealing with ambiguity, but lately that seems to be changing. “Ambiguity” is becoming a popular buzzword in hiring, so I’m trying...
-
6
8 Helpful Reminders for Every Designer to Stop Panicking and Start Thriving with AmbiguityA Retrospective on Building Out a Physical Product For the First Time With No Prior Experience
-
12
Intel 2.0: The History and Culture Gelsinger InheritsOn March 23rd, Pat Gelsinger, Intel’s new and rehired CEO, made broad, ambitious declarations for his strategies to reboot the troubled compan...
-
4
July 5, 2021 ...
-
11
Acknowledging Project Ambiguity with Frame-stormingFrame-storming is a technique which encourages questions. When this technique is incorporated into a workshop, it creates the time your team needs to air...
-
6
CSharp21 September, 2021C# tip: define Using Aliases to avoid ambiguity
-
5
Conversation Copy link Member
-
7
-
2
Stop resolving the same merge conflict multiple times during merge or rebase using git rererePublished: 2024.01.29 · 1 minute readIf you read it, you probably experienced a continued queue of resolving the same me...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK