C++ 公共基类打破元组的空基类优化 Gcc4.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."); #包括 #包括 #包括 A类{}; B类:公共A{std::uint32_t v}; C类:公共A{}; 静态_断言(sizeof(B)==4,“A有32位”); 静态断言(std::is_empty::value,“B为空”); 静态断言(sizeof(std::tuple)==4,“C应该是32位”;

C++ 公共基类打破元组的空基类优化 Gcc4.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."); #包括 #包括 #包括 A类{}; B类:公共A{std::uint32_t v}; C类:公共A{}; 静态_断言(sizeof(B)==4,“A有32位”); 静态断言(std::is_empty::value,“B为空”); 静态断言(sizeof(std::tuple)==4,“C应该是32位”;,c++,gcc,c++11,libstdc++,stdtuple,C++,Gcc,C++11,Libstdc++,Stdtuple,在本例中,最后一个断言失败,因为元组实际上大于4字节。在不破坏类层次结构的情况下,有没有办法避免这种情况?或者我必须实现我自己的pair实现,以其他方式对这种情况进行优化吗?正如您在实验中很容易看到的那样,这种优化可能很难实现。当元组是另一个类的数据成员时(特别是如果我预期将该类放入容器中),我发现这种优化最有用。是否有其他数据成员可以捆绑在这个元组中而不暴露这个事实?例如: class D { std::tuple<B, int, C, int> data_; public

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

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

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_);}
};
D类
{
std::元组数据;
公众:
B&get_B(){return std::get(data_u);}
C&get_C(){return std::get(data_);}
int&get_size(){return std::get(data_);}
int&get_age(){return std::get(data_u);}
};

对我来说,这并不能保证,
std::tuple
只有12个字节,因此
C
正在得到优化。

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

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

struct D : B, C {};
D
的大小将严格大于
B
C
的大小。要检查实际上是否有两个
A
子对象,您可以尝试向上转换到指向
A
的指针,编译器将很乐意向您的方向抛出一些模糊错误

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

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

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

也许可以help@AndyProwl,读起来很有趣,但我看不到直接的联系。它说两种常见的元组实现都使用EBCO,但没有说明EBCO的类型我在这里看到了失败。这种优化无法应用于您的情况。在您的元组中有两个
A
对象,每个对象必须有不同的地址。您不需要向代码中添加元组,您可以添加第四种类型:
struct D:B,C{};
您将看到
sizeof(D)>sizeof(B)
。我不确定这与空基类优化有什么关系。
类C
的大小必须至少为1,无论它是否派生自
类a
,以及
类C
中是否对
类C
进行了“优化”
std::tuple
不使用继承来打包参数zed将类型输入元组,因此
std::tuple
不会为EBCO增加机会。因此我不确定“GCC4.7.1对元组进行空基类优化”是什么意思。@MichaelBurr:对于空参数类型,随附的libstdc++用于将内容打包到元组中。一个实际应用程序是,例如a.两者,并将解释一些细节。不幸的是,在我的情况下,这不是一个选项,但在一般情况下很有用。在我的情况下,我放弃了从
a
中继承的内容,因为它不是绝对正确的在这种特定情况下是必要的。其他情况可能需要其他方法,包括您的方法。@MvG:继承是最滥用的构造之一。如果没有必要,就不要使用它。@DavidRodríguez dribeas:在我的情况下,共同祖先是一种简单的标记类型。我本来打算将它作为一种允许模板化运算符应用于所有类型的方法在我的类中,但没有其他类。我想我会实现一个单独的谓词模板。如果C++有概念,我可能就不太想去(ab)了。在这类事情上使用继承。但我还是会记住这一课。@MvG:这就是标准中没有添加
boost::noncopyable
的原因。虽然在许多上下文中它是免费的,但也有一些构造是有代价的,特别是在某些特定情况下,包括on,存在多重继承e在这个问题上:)