Home > Mobile >  How to solve "ambiguous base class" error while deriving from recurring template class?
How to solve "ambiguous base class" error while deriving from recurring template class?

Time:12-30

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?

CodePudding user response:

Th code is valid and obviously not ambiguous enough to fail when compiling with the latest gcc compiler. You can use static_cast<> to be more explicit when using the older compiler.

As in:

#include <iostream>
#include <string>

template<typename ...> class Foo;

template<typename T>
class Foo<T>{
public:
    void foo() const {
        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(static_cast<Foo<std::string, int>>(b));

    Bar c;
    Baz d;
    c.test(static_cast<Foo<int, std::string>>(d));   
    
    return 0;
}

You'll find the code here: https://godbolt.org/z/YjPqaKdbj

CodePudding user response:

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 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";
    }
};

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();
    }
    using baseclass = Foo<std::string, int>;
};

class Baz : public Foo<int, std::string> {
public:
    template<typename ...Args>
    void test(Foo<Args...> f) {
        f.foo();
    }
    using baseclass = Foo<int, std::string>;
};


int main() {
    Bar a;
    Bar b;
    a.test(static_cast<decltype(b)::baseclass>(b));

    Bar c;
    Baz d;
    c.test(static_cast<decltype(b)::baseclass>(b));   
    
    return 0;
}

Check it on goldbolt with gcc 9.1 and clang 9.

  • Related