Suppose I have this variadic base class-template:
template <typename ... Types>
class Base
{
public:
// The member foo() can only be called when its template
// parameter is contained within the Types ... pack.
template <typename T>
typename std::enable_if<Contains<T, Types ...>::value>::type
foo() {
std::cout << "Base::foo()\n";
}
};
The foo()
member can only be called when its template-parameter matches at least one of the parameters of Base
(the implementation of Contains
is listed at the bottom at this post):
Base<int, char>().foo<int>(); // fine
Base<int, char>().foo<void>(); // error
Now I define a derived class that inherits twice from Base, using non-overlapping sets of types:
struct Derived: public Base<int, char>,
public Base<double, void>
{};
I was hoping that when calling e.g.
Derived().foo<int>();
the compiler would figure out which base-class to use, because it is SFINAE'd out of the one that does not contain int
. However, both GCC 4.9 and Clang 3.5 complain about an ambiguous call.
My question then is two-fold:
Derived().Base<int, char>::foo<int>();
? EDIT: GuyGreer showed me that the call is disambiguated when I add two using-declarations. However, since I'm providing the base-class for the user to inherit from, this isn't an ideal solution. If at all possible, I don't want my users to have to add those declarations (which can be quite verbose and repetitive for large type-lists) to their derived classes.Implementation of Contains
:
template <typename T, typename ... Pack>
struct Contains;
template <typename T>
struct Contains<T>: public std::false_type
{};
template <typename T, typename ... Pack>
struct Contains<T, T, Pack ...>: public std::true_type
{};
template <typename T, typename U, typename ... Pack>
struct Contains<T, U, Pack ...>: public Contains<T, Pack...>
{};
Multiple Inheritance is a feature of C++ where a class can inherit from more than one classes. The constructors of inherited classes are called in the same order in which they are inherited. For example, in the following program, B's constructor is called before A's constructor.
Multiple Inheritance is a feature of an object-oriented concept, where a class can inherit properties of more than one parent class. The problem occurs when there exist methods with the same signature in both the superclasses and subclass.
The private members of a class can be inherited but cannot be accessed directly by its derived classes. They can be accessed using public or protected methods of the base class. The inheritance mode specifies how the protected and public data members are accessible by the derived classes.
For example, suppose that two classes named A and B both have a member named x , and a class named C inherits from both A and B . An attempt to access x from class C would be ambiguous. You can resolve ambiguity by qualifying a member with its class name using the scope resolution ( :: ) operator.
Here's a simpler example:
template <typename T>
class Base2 {
public:
void foo(T ) { }
};
struct Derived: public Base2<int>,
public Base2<double>
{};
int main()
{
Derived().foo(0); // error
}
The reason for that comes from the merge rules [class.member.lookup]:
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi, and merge each such lookup set S(f,Bi) in turn into S(f,C).
— [..]
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous...
Since our initial declaration set is empty (Derived
has no methods in it), we have to merge from all of our bases - but our bases have differing sets, so the merge fails. However, that rule explicitly only applies if the declaration set of C
(Derived
) is empty. So to avoid it, we make it non-empty:
struct Derived: public Base2<int>,
public Base2<double>
{
using Base2<int>::foo;
using Base2<double>::foo;
};
That works because the rule for applying using
is
In the declaration set, using-declarations are replaced by the set of designated members that are not hidden or overridden by members of the derived class (7.3.3),
There's no comment there about whether or not the members differ - we effectively just provide Derived
with two overloads on foo
, bypassing the member name lookup merge rules.
Now, Derived().foo(0)
unambiguously calls Base2<int>::foo(int )
.
Alternatively to having a using
for each base explicitly, you could write a collector to do them all:
template <typename... Bases>
struct BaseCollector;
template <typename Base>
struct BaseCollector<Base> : Base
{
using Base::foo;
};
template <typename Base, typename... Bases>
struct BaseCollector<Base, Bases...> : Base, BaseCollector<Bases...>
{
using Base::foo;
using BaseCollector<Bases...>::foo;
};
struct Derived : BaseCollector<Base2<int>, Base2<std::string>>
{ };
int main() {
Derived().foo(0); // OK
Derived().foo(std::string("Hello")); // OK
}
In C++17, you can pack expand using
declarations also, which means that this can be simplified into:
template <typename... Bases>
struct BaseCollector : Bases...
{
using Bases::foo...;
};
This isn't just shorter to write, it's also more efficient to compile. Win-win.
Though I can't tell you in detail why it doesn't work as is, I added using Base<int, char>::foo;
and using Base<double, void>::foo;
to Derived
and it compiles fine now.
Tested with clang-3.4
and gcc-4.9
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With