繁体   English   中英

C++ 模板子类和多个 inheritance 歧义

[英]C++ template subclass and multiple inheritance ambiguity

我有两个基类AB ,以及第三个 class C (实际上)从它们两者派生。 每个 class 都公开了自己的公共shared_ptr类型。

在另一个 class 我有两个向量,我想将 A 类型的对象添加到一个向量,将 B 类型的对象添加到另一个向量,并将 C 类型的对象添加到两者。 这导致了三个add方法,一个用于这三个类中的每一个。

当我尝试从C进一步派生时,我的问题出现了:

#include <iostream>
#include <memory>
#include <vector>

class A {
public:
    using shared_ptr = std::shared_ptr<A>;
    virtual ~A() {};
};

class B {
public:
    using shared_ptr = std::shared_ptr<B>;
    virtual ~B() {};
};
 
class C : virtual public A, virtual public B {
public:
    using shared_ptr = std::shared_ptr<C>;
    virtual ~C() {};
};

class D : virtual public C {
public:
    virtual ~D() {};
};

class Test {
protected:
    std::vector<A::shared_ptr> vecA;
    std::vector<B::shared_ptr> vecB;

public:
    void add(const A::shared_ptr& o) {
        std::cerr << "in A" << std::endl;
        vecA.push_back(o);
    }

    void add(const B::shared_ptr& o) {
        std::cerr << "in B" << std::endl;
        vecB.push_back(o);
    }

    void add(const C::shared_ptr& o) {
        std::cerr << "in C" << std::endl;
        vecA.push_back(o);
        vecB.push_back(o);
    }
};

int main()
{
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    auto c = std::make_shared<C>();
    auto d = std::make_shared<D>();

    Test t;
    t.add(a);
    t.add(b);
    t.add(c);
    t.add(d);
}

这不起作用 - 无法确定哪个版本的add to call 的分辨率:

test.cc:62:7: error: call to member function 'add' is ambiguous
    t.add(d);
    ~~^~~
test.cc:34:10: note: candidate function
    void add(const A::shared_ptr& o) {
         ^
test.cc:39:10: note: candidate function
    void add(const B::shared_ptr& o) {
         ^
test.cc:44:10: note: candidate function
    void add(const C::shared_ptr& o) {
         ^

我确实可以选择简单地将我的C object 分别传递给Test::add(const A::shared_ptr&)Test::add(const B::shared_ptr&)因为实际上B版本的add具有解析的附加参数重载,但我希望调用者不必记住这样做。

这种歧义可以解决吗? 我的目标环境将我限制在 C++14。

在对转换序列进行排序时,标准派生到碱基的转换序列会考虑 inheritance 链的长度,一个接近的碱基将被认为比 inheritance 链更靠前的转换序列更好。 这反过来也会影响指针和引用!

遗憾的是,由于智能指针是用户定义的类型,它们无法从这种行为中受益。 通过(有效的)用户定义的转换,所有三个重载都是可行的。 并且各个基地的“排名”不会影响过载的排名。

但这并不意味着我们不能重新引入派生到基础转换强加的排名。 我们只需要通过另一个参数来做到这一点。 通过使用标签调度,我们可以做到这一点。

我们可以定义一个辅助实用程序类型:

template<int n> struct rank : rank<n - 1> {};
template<>      struct rank<0> {};

对于任何0 <= i < j <= krank<k> -> rank<j>的转换顺序总是被认为比rank<k> -> rank<i>更好。 因此,如果我们使您的重载集不可访问,并明确对它们进行排名:

protected:
    void add(const A::shared_ptr& o, rank<0>) { /*...*/ }
    void add(const B::shared_ptr& o, rank<0>) { /*...*/ }
    void add(const C::shared_ptr& o, rank<1>) { /*...*/ }

然后,我们可以以 function 模板的形式公开另一个重载:

public:
    template<typename T>
    void add(const std::shared_ptr<T>& o) {
        return add(o, rank<10>{});
    }

它主要只是转发到一个受保护的重载,但它添加了另一个参数。 等级标签。 这也会影响重载分辨率。 尽管所有三个add重载都是可行的,但rank<10>的派生到基数的转换将影响最佳的选择。

这里是现场直播

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM