Variadic template only compiles when forward declared - c++

I have a variadic template that inherits from all template arguments:
template <typename... Ts>
struct derived : Ts...
{
};
I would also like to have a facility for expressing the type of "existing derived with added template arguments". My attempt at this is:
// Do not ODR-use (goes in namespace impl or similar)!
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
As a simple example, Added<derived<A, B>, C> should be derived<A, B, C>. I use the helper function for template argument deduction of the first parameter pack.
My problem: For some reason, I can use this successfully with incomplete types if derived has been forward declared, but not if it was defined.
Why does this code not compile:
#include <utility>
template <typename... Ts>
struct derived : Ts...
{};
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
struct A;
struct B;
struct C;
// Goal: This forward declaration should work (with incomplete A, B, C).
auto test(derived<A, B> in) -> Added<decltype(in), C>;
struct A {};
struct B {};
struct C {};
void foo()
{
auto abc = test({});
static_assert(std::is_same_v<decltype(abc), derived<A, B, C>>, "Pass");
}
Whereas this code does compile:
#include <utility>
template <typename... Ts>
struct derived;
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
struct A;
struct B;
struct C;
// Goal: This forward declaration should work (with incomplete A, B, C).
auto test(derived<A, B> in) -> Added<decltype(in), C>;
template <typename... Ts>
struct derived : Ts...
{};
struct A {};
struct B {};
struct C {};
void foo()
{
auto abc = test({});
static_assert(std::is_same_v<decltype(abc), derived<A, B, C>>, "Pass");
}
For convenience, here are both cases at once (comment in/out #define FORWARD_DECLARED): https://godbolt.org/z/7gM52j
I do not understand how code could possibly become illegal by replacing a forward declaration by the respective definition (which would otherwise just come later).

Evg's observation hits the nail on the head: the problem here is ADL. It's actually the same problem I ran into with this question.
The issue is this: we have an unqualified call here:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
// ^^^^^^^^^^^
We know it's a function template because we find it in using regular lookup, so we don't have to deal with the whole "is < an operator or a template introducer" question. However, because it's an unqualified call, we also must perform argument-dependent lookup.
ADL needs to look into the associated namespaces of all the arguments, which seems fine - we don't need complete types for that. But ADL also needs to look for potential friend functions and function templates defined within the classes. After all, this needs to work:
struct X {
friend void foo(X) { }
};
foo(X{}); // must work, call the hidden friend defined within X
As a result, in our call in question:
auto test(derived<A, B> in) -> Added<decltype(in), C>;
We have to instantiate derived<A, B>... but that type inherits from two incomplete classes, which we can't do. That's where the problem is, that's where we fail.
This is why the forward declaration version works. template <typename... T> struct derived; is incomplete, so just trying to look inside of it for friend functions trivially finds nothing - we don't need to instantiate anything else.
Likewise, a version where derived was complete but didn't actually derive from anything would also work.
Thankfully, this is fixable in this context with what Evg suggested. Make a qualified call:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(::addedHelper<NewInputs...>(std::declval<ExistingInput>()));
This avoids ADL, which you didn't even want. Best case, you're avoiding doing something that has no benefit to you. Bad case, your code doesn't compile. Evil case, for some inputs you accidentally call a different function entirely.
Or just use Boost.Mp11's mp_push_back

Related

How to conditionally get template type of multiple base class with multiple inheritance

This is NOT a duplicate of link
Consider the following code:
#include <type_traits>
template <typename... Bases>
struct Overloads : public Bases... {};
template <typename T>
struct A {
using AType = T;
};
template <typename T>
struct B {
using BType = T;
};
template <typename T>
struct C {
using CType = T;
};
template <typename OverloadsType>
struct Derived : public OverloadsType {
};
int main() {
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::AType, int>);
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::BType, float>);
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::CType, char>);
// ???
static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::AType, void>);
}
Demo: Link
For the last line, I need to detect that Derived<Overloads<B<float>, C<char>>> is NOT derived from A, so I want typename Derived<Overloads<B<float>, C<char>>>::AType to be void or something (it fails to compile)
How can I do this?
My precise use case is: 1. Determine if Derived is derived from A<T> for some T. 2. If so, figure out that T.
With C++20 concepts, this isn't too difficult. You need a function that takes A<T> as a parameter. There need not be a function definition; we're just using template argument deduction. It will never be called:
template<typename T>
T getDerivedFromAType(A<T> const&); //Not implemented, since we will never call it.
Any type U for which getDerivedFromAType(u) works will either be an A<T> itself or a type derived from A<T> (from a single A<T> of course). So we can build a concept out of it:
template<typename U>
concept IsDerivedFromA = requires(U u)
{
getDerivedFromAType(u);
};
So long as nobody writes an overload of getDerivedFromAType, you're fine. This function ought to be in a detail namespace or have something else that lets people know that it is off-limits.
To get the actual T used by A... well, there's a reason why the function returned T. We simply need a using statement that calculates the return type of the function call:
template<IsDerivedFromA T>
using BaseAType = decltype(getDerivedFromAType(std::declval<T>()));
Notice that the template is guarded by our concept.

how to make argument deduction work for derived class which using base class constructor?

When derived class using base class constructor the deduction seems always fail. However, when the base class have lots of constructors it is very clumsy to re-define all the constructors. It is also a pain when base class are quickly evolved with new constructors. The old question was asked more than 2 years ago, so I wonder: is there any work around for this in 2020 when c++17 and c++2a are available?
template<typename ...As>
class base_t
{
public:
base_t(As... args){}
};
template<typename ...As>
class A_t: public base_t<As...>
{
public:
A_t(As... args): base_t<As...>{args...} {};
};
template<typename ...As>
class B_t: public base_t<As...>
{
using base_t<As...>::base_t;
};
int main()
{
base_t a{1, 2.0f};
A_t{1, 2.0f};
B_t{1, 2.0f}; //fails unless explicitly specialize the template
return 0;
}
updates according to #Sam and #Barry:
The deduction guide is very helpful. However, for a little be more complicate situation, it still runs out of control:
template <typename A>
struct D_t {
A x;
D_t(A x) :x{x} {}
};
template<typename A, typename B>
class base2_t
{
public:
base2_t(A a, B b){std::cout << "1\n";}
base2_t(A a, D_t<B> c, int x){std::cout << "2\n";}
base2_t(A a, B b, int x){std::cout << "3\n";}
base2_t(A a, B b, int x, float y){std::cout << "4\n";}
explicit base2_t(A(*fp)(B)){std::cout << "5\n";}
// if we have lots of similar things like above
// we will quickly end up write lots of different
// guides.
};
template<typename A, typename B>
class C_t: public base2_t<A, B>
{
using base2_t<A, B>::base2_t;
};
template<typename A, typename B, typename ...As>
C_t(A, B, As...)->C_t<A, B>;
template<typename A, typename B>
C_t(A(*)(B))->C_t<A, B>;
float func1(int x)
{
return x;
}
int main()
{
C_t{1, 2.0f, 3};
base2_t{1, D_t{2.0f}, 3};
C_t{1, D_t{2.0f}, 3}; // this is wrong, we have to deal with it by checking types and write different guides.
base2_t{&func1};
C_t{&func1};
}
till 2023, the proposal is accepted in c++23 P2582R1 But neither gcc nor llvm has implemented it yet. But there is a hope that in the middle of this year this issue will finally get fixed.
Being able to inherit deduction guides from base classes was proposed for c++20. However, this feature didn't make it in, as the last line says:
The wording for CTAD from inherited constructors was not finalized in time for the C++20 committee draft, and will be published in a separate wording paper at a later point in time.
So as of now, you will need to provide deduction guides for the derived class explicitly (or define the constructor as you did for A_t). Hopefully, this will be fixed in c++23.
Unfortunately, there's no generic way to inherit the base class' deduction guides in C++20. There was a proposal for it (P1021), but it didn't make it for C++20.
Without that proposal, it's tempting to suggest something like:
template <typename... Args>
Derived(Args&&... args) -> mp_rename<decltype(Base((Args&&)args...)), Derived>;
That is, figure out the type that you get from passing all the args into Base using class template argument deduction there - and then just rename whatever Base<Ts...> you get into Derived<Ts...>. But the grammar for a deduction-guide is:
explicit-specifier opt template-name ( parameter-declaration-clause ) -> simple-template-id ;
And there's the added restriction in [temp.deduct.guide]/3 that:
The template-name shall be the same identifier as the template-name of the simple-template-id.
So a deduction guide for Derived has to end up with exactly -> Derived<Args...>. There's no way to stick any cleverness in there.
Without direct language help, and without the ability to do any template metaprogramming here, you're left to just carefully write the explicit deduction guides yourself. Thankfully, at least here you can write static_asserts to make sure you did the job right:
template <typename... Ts>
inline constexpr bool matches = std::is_same_v<
decltype(Base(std::declval<Ts>()...)),
mp_rename<decltype(Derived(std::declval<Ts>()...)), Base>>;
static_assert(matches<int>); // or whatever is reasonable for your specific types
One option is an explicit deduction guide (C++17):
template<typename ...As>
class base_t
{
public:
base_t(As... args){}
};
template<typename ...As>
class B_t: public base_t<As...>
{
using base_t<As...>::base_t;
};
template<typename ...As>
B_t( As...) -> B_t<As...>;
int main()
{
base_t a{1, 2.0f};
B_t b{1, 2.0f};
B_t<int, float> bb=b;
return 0;
}
More work will probably be needed to correctly deal with references, probably by throwing in a std::remove_reference_cv (remove_cvref_t for C++20), or a std::decay_t somewhere in there...
You could use #Barry's rename Idea with a layer of indirection:
template<class... Args>
struct ctad_tag{};
template<class... Args>
struct deriv : base<Args...> {
using base<Args...>::base;
};
template<class... Args>
struct deriv<ctad_tag<Args...>> : deriv<Args...> {
using deriv<Args...>::deriv;
};
template<class... Args>
deriv(Args&&... args)
-> deriv<rename_t<decltype(base(std::forward<Args>(args)...)), ctad_tag>>;
But it's a little hacky, since you'll end up with a deriv<ctad_tag<...>> instead of a deriv<...>. Still, depending on the use case this might be fine, so I'll leave this here.
Working example: https://godbolt.org/z/ygWpJU

C++ using declaration for parameter pack

I would like to define a class which inherits from a bunch of classes but which does not hide some specific methods from those classes.
Imagine the following code:
template<typename... Bases>
class SomeClass : public Bases...
{
public:
using Bases::DoSomething...;
void DoSomething(){
//this is just another overload
}
};
The problem is now if just one class does not have a member with the name DoSomething I get an error.
What I already tried was emulating an "ignore-if-not-defined-using" with a macro and SFINAE but to handle all cases this becomes very big and ugly!
Do you have any idea to solve this?
It would be really nice if I could define: "Hey using - ignore missing members".
Here I have some sample code: Godbolt
The problem with Jarod42's approach is that you change what overload resolution looks like - once you make everything a template, then everything is an exact match and you can no longer differentiate between multiple viable candidates:
struct A { void DoSomething(int); };
struct B { void DoSomething(double); };
SomeClass<A, B>().DoSomething(42); // error ambiguous
The only way to preserve overload resolution is to use inheritance.
The key there is to finish what ecatmur started. But what does HasDoSomething look like? The approach in the link only works if there is a single, non-overloaded, non-template. But we can do better. We can use the same mechanism to detect if DoSomething exists that is the one that requires the using to begin with: names from different scopes don't overload.
So, we introduce a new base class which has a DoSomething that will never be for real chosen - and we do that by making our own explicit tag type that we're the only ones that will ever construct. For lack of a better name, I'll name it after my dog, who is a Westie:
struct westie_tag { explicit westie_tag() = default; };
inline constexpr westie_tag westie{};
template <typename T> struct Fallback { void DoSomething(westie_tag, ...); };
And make it variadic for good measure, just to make it least. But doesn't really matter. Now, if we introduce a new type, like:
template <typename T> struct Hybrid : Fallback<T>, T { };
Then we can invoke DoSomething() on the hybrid precisely when T does not have a DoSomething overload - of any kind. That's:
template <typename T, typename=void>
struct HasDoSomething : std::true_type { };
template <typename T>
struct HasDoSomething<T, std::void_t<decltype(std::declval<Hybrid<T>>().DoSomething(westie))>>
: std::false_type
{ };
Note that usually in these traits, the primary is false and the specialization is true - that's reversed here. The key difference between this answer and ecatmur's is that the fallback's overload must still be invocable somehow - and use that ability to check it - it's just that it's not going to be actually invocable for any type the user will actually use.
Checking this way allows us to correctly detect that:
struct C {
void DoSomething(int);
void DoSomething(int, int);
};
does indeed satisfy HasDoSomething.
And then we use the same method that ecatmur showed:
template <typename T>
using pick_base = std::conditional_t<
HasDoSomething<T>::value,
T,
Fallback<T>>;
template<typename... Bases>
class SomeClass : public Fallback<Bases>..., public Bases...
{
public:
using pick_base<Bases>::DoSomething...;
void DoSomething();
};
And this works regardless of what all the Bases's DoSomething overloads look like, and correctly performs overload resolution in the first case I mentioned.
Demo
How about conditionally using a fallback?
Create non-callable implementations of each method:
template<class>
struct Fallback {
template<class..., class> void DoSomething();
};
Inherit from Fallback once for each base class:
class SomeClass : private Fallback<Bases>..., public Bases...
Then pull in each method conditionally either from the base class or its respective fallback:
using std::conditional_t<HasDoSomething<Bases>::value, Bases, Fallback<Bases>>::DoSomething...;
Example.
You might add wrapper which handles basic cases by forwarding instead of using:
template <typename T>
struct Wrapper : T
{
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args) const
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args)
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
// You might fix missing noexcept specification
// You might add missing combination volatile/reference/C-elipsis version.
// And also special template versions with non deducible template parameter...
};
template <typename... Bases>
class SomeClass : public Wrapper<Bases>...
{
public:
using Wrapper<Bases>::DoSomething...; // All wrappers have those methods,
// even if SFINAEd
void DoSomething(){ /*..*/ }
};
Demo
As Barry noted, there are other drawbacks as overload resolution has changed, making some call ambiguous...
Note: I proposed that solution as I didn't know how to create a correct traits to detect DoSomething presence in all cases (overloads are mainly the problem).
Barry solved that, so you have better alternative.
You can implement this without extra base classes so long as you’re willing to use an alias template to name your class. The trick is to separate the template arguments into two packs based on a predicate:
#include<type_traits>
template<class,class> struct cons; // not defined
template<class ...TT> struct pack; // not defined
namespace detail {
template<template<class> class,class,class,class>
struct sift;
template<template<class> class P,class ...TT,class ...FF>
struct sift<P,pack<>,pack<TT...>,pack<FF...>>
{using type=cons<pack<TT...>,pack<FF...>>;};
template<template<class> class P,class I,class ...II,
class ...TT,class ...FF>
struct sift<P,pack<I,II...>,pack<TT...>,pack<FF...>> :
sift<P,pack<II...>,
std::conditional_t<P<I>::value,pack<TT...,I>,pack<TT...>>,
std::conditional_t<P<I>::value,pack<FF...>,pack<FF...,I>>> {};
template<class,class=void> struct has_something : std::false_type {};
template<class T>
struct has_something<T,decltype(void(&T::DoSomething))> :
std::true_type {};
}
template<template<class> class P,class ...TT>
using sift_t=typename detail::sift<P,pack<TT...>,pack<>,pack<>>::type;
Then decompose the result and inherit from the individual classes:
template<class> struct C;
template<class ...MM,class ...OO> // have Method, Others
struct C<cons<pack<MM...>,pack<OO...>>> : MM...,OO... {
using MM::DoSomething...;
void DoSomething();
};
template<class T> using has_something=detail::has_something<T>;
template<class ...TT> using C_for=C<sift_t<has_something,TT...>>;
Note that the has_something here supports only non-overloaded methods (per base class) for simplicity; see Barry’s answer for the generalization of that.

How to restrict a method template parameter to types which appear in the template arguments of enclosing structure? [duplicate]

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:
Why can't the compiler resolve this ambiguity (general interest)?
What can I do to make this work, without having to write 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...>
{};
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

Disambiguate class-member in multiple inheritance

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:
Why can't the compiler resolve this ambiguity (general interest)?
What can I do to make this work, without having to write 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...>
{};
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