3

gcc 4.7.1 对元组进行空基类优化,我认为这是一个非常有用的特性。但是,这似乎有一个意想不到的限制:

#include <tuple>
#include <cstdint>
#include <type_traits>
class A { };
class B : public A { std::uint32_t v_; };
class C : public A { };
static_assert(sizeof(B) == 4,                "A has 32 bits.");
static_assert(std::is_empty<C>::value,       "B is empty.");
static_assert(sizeof(std::tuple<B, C>) == 4, "C should be 32 bits.");

在这种情况下,最后一个断言失败,因为元组实际上大于 4 个字节。有没有办法在不破坏类层次结构的情况下避免这种情况?或者我是否必须实现我自己的配对实现,以其他方式针对这种情况进行优化?

4

2 回答 2

6

空对象必须占用一些空间的原因是两个不同的对象必须具有不同的地址。例外情况是派生类型的基子对象可以与派生的完整对象具有相同的地址(如果派生类型的第一个非静态成员与基[*]的类型不同。空基础优化使用它来删除任意添加到空基础的额外空间,以确保sizeof x!=0对于任何完整对象。

在您的情况下,元组包含两个 A子对象,一个是基础,C另一个是基础B,但它们不同,因此它们必须具有不同的地址。这两个对象都不是另一个对象的基本子对象,因此它们不能具有相同的地址。你甚至不需要使用std::tuple来看到这个效果,只需创建另一个类型:

struct D : B, C {};

的大小D将严格大于 和 的B大小C。要检查实际上是否有两个A子对象,您可以尝试向上转换为指向的指针,A编译器会很乐意向您的方向吐出一些歧义错误。

[*]出于同样的原因,该标准也明确禁止这种情况:

struct A {};
struct B : A { A a; };

同样,在这种情况下,在 type 的每个完整对象中B,都有两个A对象,它们必须具有不同的地址。

于 2013-02-25T21:59:03.757 回答
2

正如您在实验中很容易看到的那样,这种优化可能很难实现。当 是另一个类的数据成员时,我发现这种优化最有用tuple(特别是如果我希望将该类放入容器中)。是否有其他数据成员可以捆绑在tuple其中而不暴露该事实?例如:

class D
{
    std::tuple<B, int, C, int> data_;
public:
    B& get_B() {return std::get<0>(data_);}
    C& get_C() {return std::get<2>(data_);}
    int& get_size() {return std::get<1>(data_);}
    int& get_age() {return std::get<3>(data_);}
};

对我来说,这绝不是保证,std::tuple<B, int, C, int>只有 12 个字节,所以C正在优化。

于 2013-02-25T21:57:20.833 回答