The following code is failing to compile on both GCC 9.1 and Clang 9.0:
#include <iostream>
#include <string>
template<typename ...> class Foo;
template<typename T>
class Foo<T>{
public:
void foo() {
std::cout << "foo (1)\n";
}
};
template<typename T, typename ... Tail>
class Foo<T, Tail...> : public Foo<Tail...> {
public:
void foo() {
std::cout << "foo (2)\n" ;
}
};
class Bar : public Foo<std::string, int> {
public:
template<typename ...Args>
void test(Foo<Args...> f) {
f.foo();
}
};
class Baz : public Foo<int, std::string> {
public:
template<typename ...Args>
void test(Foo<Args...> f) {
f.foo();
}
};
int main() {
Bar a;
Bar b;
a.test(b);
Bar c;
Baz d;
c.test(d);
return 0;
}
However, it works fine with latest versions.
Is this code actually valid? Why is it considered ambiguous? Is there any workaround that would work with previous versions of GCC and Clang?
This is a possible workaround for both GCC and Clang: an explicit static_cast
to the base class. For convenience I inserted a type alias in the derived base* class, such that you can call test
with the same syntax in both cases.
#include <iostream>
#include <string>
template<typename ...> class Foo;
template<typename T>
class Foo<T>{
public:
void foo() {
std::cout << "foo (1)\n";
}
using footype = Foo<T>;
};
template<typename T, typename ... Tail>
class Foo<T, Tail...> : public Foo<Tail...> {
public:
void foo() {
std::cout << "foo (2)\n" ;
}
using footype = Foo<T, Tail...>;
};
class Bar : public Foo<std::string, int> {
public:
template<typename ...Args>
void test(Foo<Args...> f) {
f.foo();
}
};
class Baz : public Foo<int, std::string> {
public:
template<typename ...Args>
void test(Foo<Args...> f) {
f.foo();
}
};
int main() {
Bar a;
Bar b;
a.test(static_cast<decltype(b)::footype>(b));
Bar c;
Baz d;
c.test(static_cast<decltype(d)::footype>(d));
return 0;
}
Check it on goldbolt with gcc 9.1 and clang 9.
*Edit after comment.