Here's a cut down example of what I'm trying to do:
#include <string>
#include <iostream>
#include <type_traits>
template <typename T>
class foo
{
public:
template <typename U>
typename std::enable_if<std::is_same<T, U>::value>::type
bar(const U& t)
{
std::cout << t << "\n";
}
};
template <typename... Args>
class baz
: public foo<Args>...
{
};
int main()
{
baz<double, std::string> b;
b.bar(1.0);
}
This gives me ambiguous function errors:
error: request for member 'bar' is ambiguous b.bar(1.0); note: candidates are: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = std::basic_string<char>] note: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = double]
My questions are twofold:
U
not deduced? I'm supposing that it's due to ordering of template deduction and overload resolution, but can someone explain this?I think the error message is misleading. The problem is actually name bar
is available in multiple base classes and you've not used using
directive to bring the names you want into the derived class scope.
Here is one working solution:
template <typename X, typename... Args>
class baz : public foo<X>, public baz<Args...>
{
public:
using foo<X>::bar; //bring the name from the first base
using baz<Args...>::bar; //bring the name from the second base
};
template <typename X>
class baz<X> : public foo<X> //specialization for one argument
{
//no using directive needed, as there is one base only!
};
Complete Demo
The problem has nothing to do with variadic templates, template argument deduction, or the like. It is that member functions of the same name from different base classes don't overload. Minimized example:
struct foo {
void f(int &);
};
struct bar {
void f(const int &);
};
struct foobar : foo, bar { };
int main(){
foobar fb;
int i;
fb.f(i); // clang complains: error: member 'f' found in multiple base classes of different types
}
Since in your code, foo<double>
and foo<std::string>
are distinct types, and lookup for bar
finds a declaration in each, your code is ill-formed.
A possible fix is to write a baz::bar
that explicitly dispatches to the appropriate foo::bar
:
template <typename... Args>
class baz
: public foo<Args>...
{
public:
template <typename U>
void
bar(const U& t)
{
foo<U>::bar(t);
}
};
You can SFINAE baz::bar
on U
being one of the types in Args
, if desired.
Another possible solution is to use the recursive implementation shown in Nawaz's answer.
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