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

空のオブジェクトがある程度のスペースを取る必要がある理由は、2つの異なるオブジェクトが異なるアドレスを持っている必要があるためです。例外は、派生型のベースサブオブジェクトが派生完全オブジェクトと同じアドレスを持つことができることです(派生型の最初の非静的メンバーがベース[*]と同じタイプでない場合。空ベース最適化では、これを使用して、空のベースに追加された追加のスペースを任意に削除し、sizeof x!=0完全なオブジェクトを確保します。

あなたの場合、タプルは2つの Aサブオブジェクトを保持しており、一方はのベースでありC、もう一方はのベースですBが、それらは異なるため、異なるアドレスを持っている必要があります。これらの2つのオブジェクトはいずれも、もう一方のベースサブオブジェクトではないため、同じアドレスを持つことはできません。std::tupleこの効果を確認するために使用する必要はありません。別のタイプを作成するだけです。

struct D : B, C {};

のサイズは、とDの両方のサイズよりも厳密に大きくなります。実際に2つのサブオブジェクトがあることを確認するには、へのポインターへのアップキャストを試みることができます。コンパイラーは、あいまいなエラーを喜んであなたの方向に吐き出します。BCAA

[*]同じ理由で、標準ではこのケースも明示的に禁止されています。

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

この場合も、タイプの完全なオブジェクトごとにB2つの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 に答える