5

unionメンバーは、デストラクタまたはコンストラクタを持つことはできません。したがって、コンストラクターがある場合、次のクラスFooを自分でテンプレート化することはできません。MyClassMyClass

template<class T>
struct Foo {
  T val;
  Foo(T val_) : val(val_) {}
  size_t hash() const {
    union {T f; size_t s;} u = { val };
    return u.s;
  }
};
struct MyClass {
  bool a;
  double b;
  MyClass(bool a_, double b_) : a(a_), b(b_) {}
};

とにかくそれを行うと、次のエラーが発生します。

member 'MyClass Foo<T>::hash() const 
[with T = MyClass]::<anonymous union>::f' with constructor 
not allowed in union

それを回避するためにMyClass、最初に物事をコピーする厄介な構築関数を作成しました。

struct MyClass {
  bool a;
  double b;
};
MyClass createMyClass(bool a, double b) {
  MyClass m;
  m.a = a;
  m.b = b;
  return m;
}

createMyClassしかし、この関数を使用するよりも良い方法があるかどうか疑問に思っています。コンストラクターはより効率的であり、重要なコンポーネントとして、MyClassFoo<MyClass>のコードで何百万回も構築されます。

std::ペア

で使用できることに少し驚いています:std::pairunion

Foo<std::pair<bool, double> > f2(std::make_pair(true, 3.12));

私の知る限り、std::pairコードを参照)コンストラクターはありますか?

4

4 に答える 4

10

編集: 私の最初のスタンスstd::pairは間違っていました。組合では許可されるべきではありません。クラスが共用体の有効なメンバーであるためには、標準 9.5.1 に従って自明なコンストラクターが必要です。自明なコンストラクターの定義は、段落 12.1.5 から、次のとおりです。

クラス X に対してユーザーが宣言したコンストラクターがない場合は、既定のコンストラクターが暗黙的に宣言されます。暗黙的に宣言された既定のコンストラクターは、inline publicそのクラスのメンバーです。 暗黙的に宣言されたデフォルトのコンストラクターであり、次の場合、コンストラクターは自明です。

  • そのクラスには仮想関数も仮想基本クラスもありません。
  • そのクラスの直接の基底クラスはすべて自明なコンストラクターを持ち、
  • クラス型 (またはその配列) であるそのクラスのすべての非静的データ メンバーに対して、そのような各クラスには自明なコンストラクターがあります。

パラグラフ 20.2.2.2 は、次のコンストラクターがペアで使用可能でなければならないと述べています。

pair(const T1& x, const T2& y);

このコンストラクターが提供されるとすぐに、デフォルトのコンストラクターは暗黙的に宣言されません。

ここで面白いのは、私のコンパイラ (Visual Studio 2008) がstd::pair特別な扱いをしているように見えることです。実装からコードをコピーstd::pairして自分の名前空間 foo に配置すると、ユニオンは機能しません:)

namespace foo {
    template<class _Ty1, class _Ty2> struct pair {
        typedef _Ty1 first_type;
        typedef _Ty2 second_type;
        pair() : first(_Ty1()), second(_Ty2()) {
        }
    }
}

//This doesn't work in VC2008
union Baz {
    foo::pair<bool, double> a;
    int b;
}
//This works in VC2008
union Buz {
    std::pair<bool, double> a;
    int b;
}

あなたのソリューションは、この問題を回避する一般的な方法です。私は通常、通常のコンストラクター構文を部分的に模倣するために、クラス名の前にC(construct の略) を追加しますCMyClass(a, b)

Steve と Matthieu が指摘しているように、あなたは非常に優れたハッシュ関数を使用していません。第一に、実際の保証はありません (私が間違っている場合は修正してください) 。ユニオンfsは、同じメモリ空間を部分的に占有することさえあります。第二に、実際にはそれらがおそらく最初のmin(sizeof(s), sizeof(f))バイトを共有する場合でも、これはMyClass値の一部のみをハッシュしています。この場合、 の値をハッシュします。bool aこの場合、2 つのオプションがあります。

  1. コンパイラはintの内部表現として を使用しboolます。この場合、ハッシュ関数は 2 つの値のみを返します。1 つは true で、もう 1 つは false です。
  2. コンパイラはchar、 の内部表現として使用しboolます。この場合、値はおそらく少なくとも にパディングされsizeof(int)ます。ゼロの場合は 1 と同じ状況になります。またはMyClassが割り当てられたときにスタック上にあるランダム データで、同じ入力に対してランダムなハッシュ値を取得することを意味します。 .

の値全体でハッシュする必要がある場合は、スティーブが提案するようにデータを一時バッファーにコピーしてから、ここでT説明した可変長ハッシュ関数のいずれかを使用します。

于 2009-12-11T14:15:20.107 に答える
4

私はこれを置き換えます:

size_t hash() const {
    union {T f; size_t s;} u = { val };
    return u.s;
}

これとともに:

size_t hash() const {
    size_t s = 0;
    memcpy(&s, &val, std::min(sizeof(size_t), sizeof(T)));
    return s;
}

2 つのサイズのうち、大きい方ではなく小さい方をコピーします。memcpy がコンパイラに組み込まれている場合は、最適化に適しています。ただし、最も重要なことは、T がどのようなコンストラクターを持っているかは問題ではありません。

ただし、T が大きな型の場合、これは適切なハッシュ関数ではありません。boolMyClass の例では、実装でとが同じサイズであることがわかる場合がありますsize_t。したがって、double はハッシュにまったく参加しないため、可能なハッシュ値は 2 つだけです。

それでも、さらに悪化する可能性があります。T に仮想関数がある場合、おそらくすべてのインスタンスが同じ値 (vtable のアドレス) にハッシュされることがわかります...

于 2009-12-11T17:56:24.000 に答える
2

組合員としての使用については、std::pair禁止すべきだと思います。標準は次のように述べています(§12.1):

共用体メンバーは、非自明なコンストラクターを持つクラス型 (またはその配列) であってはなりません。

したがって、デフォルトのコンストラクターが暗黙的に宣言されなくなるため、ユーザー定義のコンストラクターを持つクラスを共用体で使用することはできません。(§20.2.2)の仕様でstd::pairは、ペアの実装は両方の値を初期化するためにパラメーター化されたコンストラクターを提供する必要があることが明示的に述べられています。したがって、使用するペア実装またはユニオン実装のいずれかが標準に準拠していません。

NB : Comeau で指定したコードをテストすると、次のエラーが発生します。

"ComeauTest.c", line 8: error: invalid union member -- class
          "std::pair<bool, double>" has a disallowed member function
      union {T f; size_t s;} u = { val };
               ^
          detected during instantiation of "unsigned int Foo<T>::hash() const
                    [with T=std::pair<bool, double>]" at line 22
于 2009-12-11T14:35:20.713 に答える
2

質問は 1 つだけです。なぜ union を使用するのですか?

私が理解していることから、ハッシュはオブジェクトの最初の数バイトに対応する必要があります。これを行う場合、次の理由はありません。

size_t hash() const {
  return reinterpret_cast<size_t>(val);
}

sizeof(T)スタックにサイズのオブジェクトが割り当てられないため、同じトリックをより効率的に(私が思うに)達成するはずです。

于 2009-12-11T15:53:23.970 に答える