c++ 如何解决从循环模板类派生时出现的“基类不明确”错误?

6kkfgxo0  于 2023-01-03  发布在  其他
关注(0)|答案(3)|浏览(325)

以下代码在GCC 9.1和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;
}

不过,它与最新版本一起工作得很好。

这段代码实际上有效吗?为什么它被认为是不明确的?有没有任何变通办法可以在以前版本的GCC和Clang中使用?

q9yhzks0

q9yhzks01#

另一个解决方案是稍微放松一下test()模板函数的要求。
例如:

#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 T>
    void test(T&& f) {  // more relaxed requirements as temporary fix
        f.foo();        // until upgrade to newer compiler.
    }
};

class Baz : public Foo<int, std::string> {
public:
    template<typename T>
    void test(T&& f) {
        f.foo();
    }
};

class Buzz : public Foo<int>
{
    template<typename T>
    void test(T&& f) {
        f.foo();
    }
};

int main() {
    Bar a;
    Bar b;
    a.test(b);

    Bar c;
    Baz d;
    c.test(d);   

    Buzz e;
    c.test(e);
    
    return 0;
}

输出:

foo (2)
foo (2)
foo (1)
wgx48brx

wgx48brx2#

这是GCC和Clang的一个可能的解决方法:为方便起见,我在派生的base* 类中插入了一个类型别名,这样您就可以在两种情况下使用相同的语法调用test

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

gcc 9.1clang 9在goldbolt上检查它。

  • 评论后编辑。
ftf50wuq

ftf50wuq3#

代码是有效的,而且显然不会模糊到在使用最新的gcc编译器编译时失败。在使用较旧的编译器时,可以使用static_cast〈〉来更显式。
例如:

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

你可以在这里找到代码:https://godbolt.org/z/YjPqaKdbj

相关问题