使用压缩对进行不明确的基类转换

时间:2015-03-01 00:37:07

标签: c++ c++11

所以我尝试使用empty base optimization创建压缩对。我希望如果班级ab为空,那么compressed_pair<a, b>也是空的。所以我将压缩对定义为:

template <class First, class Second>
struct compressed_pair : First, Second 
{
    compressed_pair() {}
    compressed_pair(const First& x, const Second & y)
    : First(x), Second(y) 
    {}
    First& first()  { return *this; }
    Second& second() { return *this; }
};

但是,如果其中一种类型从另一种继承,则会变得模棱两可。例如,当我编译这个程序时:

struct a
{};

struct b : a
{};

int main()
{
    compressed_pair<a, b> p;
    auto x = p.first();
}

我从clang那里得到了这个错误:

compressed_pair.cpp:8:30: error: ambiguous conversion from derived class 'compressed_pair<a, b>' to base class 'a':
    struct compressed_pair<struct a, struct b> -> struct a
    struct compressed_pair<struct a, struct b> -> struct b -> struct a
    First& first()  { return *this; }
                             ^~~~~
compressed_pair.cpp:21:16: note: in instantiation of member function 'compressed_pair<a, b>::first' requested here
    auto x = p.first();
               ^

那么我怎样才能避免模糊转换并且仍然compressed_pair<a, b>为空?

2 个答案:

答案 0 :(得分:2)

您遇到的问题是compressed_pair可以应用转换的两个基础。您需要能够驱动编译器选择其中一个。首先要想到的是添加另一层可用作选择器的继承:

template <int N, typename T>
struct element : T {};

template <typename T, typename U>
struct compressed_pair : element<0, T>, element<1, U> { ... };

然后您对访问者的实现可能是:

template <typename T, typename U>
T& compressed_pair<T,U>::first() {
    return static_cast<element<0,T>&>(*this);
}

存取器内的演员强制选择两个直接基地之一element<0,T>。从那时起,只有一个T类型的基础。

您还可以使用将访问者移动到该中间步骤,而不是将其置于完整类型中。最后,通过专业化,您应该能够为支持非类类型的element提供不同的实现,因为您当前的实现会阻塞类似int的类型。

答案 1 :(得分:0)

使用virtual继承来避免歧义或要求FirstSecond不相互派生。这是一个或两个决定。您需要决定是否避免歧义或允许空基优化对您来说更重要。

一般来说,为了避免歧义,不止一次继承的那个(即你的compressed_pair和另一个结构类型)需要是一个虚拟基础。由于模板可能使用任何内容进行实例化,这意味着FirstSecond必须是compressed_pair的虚拟基础,而a必须是virtual b的基础。

如果您始终知道Second可能来自First,但First永远不会从Second派生,您可以稍微放松一下(再次出现歧义的代价) ,如果First来自Second)。

请记住,虚拟继承确实会对类的工作方式施加一些限制。例如,它影响基础构造的顺序,需要任何后来派生的类来管理通过基类继承的虚拟基础的构造等。