3

列挙値である静的メンバーを持つクラスがたくさんあります。そして、この列挙型をキーとして別の場所にマップを持っています。関数でテンプレート パラメーターを使用してマップにアクセスすると、未定義の参照が取得されます。

明確にするために、単純化された動作しない例を次に示します。

template<int T>
struct A
  {
    static const int Type = T;
  }

template<class T>
void fun()
  {
    cout << map_[T::Type] << endl;
  }

map<int, string> map_{{1337, "1337"}};

主要 :

fun<A<1337>();

私に与えます(g ++ 4.7):

undefined reference to `(anonymous namespace)::A<1337>::Type'

ただし、これ:

template<class T>
void fun()
  {
    auto key = T::Type;
    cout << map_[key] << endl;
  }

コンパイルして印刷する1337

誰かが私にこの振る舞いを説明できますか?

4

2 に答える 2

7

を使用する場合は、次のように定義するT::Type必要があります。

template<int T>
struct A
{
   static const int Type = T;
}

template <int T>
const int A<T>::Type;

A<T>はい、イニシャライザを!内でインラインで提供したにもかかわらずです。

これに気付いていない可能性がある理由は、2番目のケースで同じ問題が発生しないのと同じ理由です。左辺値から右辺値への即時変換により、標準ではコンパイラが要件を最適化できます。実行時に参照しType、代わりにコンパイル時に値を選択できます。リンカーは定義を検索する必要がなく、エラーは発生しません。


[C++11: 9.4.2/2]: クラス定義内の静的データ メンバーの宣言は定義ではなく、 cv 修飾された void 以外の不完全な型である可能性があります。静的データ メンバーの定義は、メンバーのクラス定義を囲む名前空間スコープに表示されます。名前空間スコープでの定義では、静的データ メンバーの名前は、:: 演算子を使用してそのクラス名で修飾されます。静的データ メンバーの定義の初期化式は、そのクラス (3.3.7) のスコープ内にあります。[..]

[C++11: 9.4.2/3]: 不揮発性const staticデータ メンバーが整数型または列挙型の場合、クラス定義でのその宣言は、代入式であるすべての初期化子句が定数式 (5.19) であるブレースまたはイコール初期化子を指定できます。リテラル型のメンバーは、指定子static dataを使用してクラス定義で宣言できます。constexprその場合、その宣言は、割り当て式であるすべての初期化子節が定数式であるブレースまたは等号初期化子を指定するものとします。[ 注:どちらの場合も、メンバーは定数式に現れる場合があります。—終わりのメモ] メンバーは、プログラムで ODR 使用 (3.2) され、名前空間スコープ定義に初期化子が含まれていない場合でも、名前空間スコープで定義されます。

[C++11: 3.2/2]: [..]潜在的に評価される式として現れる名前を持つ変数は、それが定数式 (5.19) に現れるための要件を満たし、左辺値から右辺値への変換 (4.1) が即座に行われるオブジェクトでない限り、odr で使用されます。適用。[..]

于 2013-05-24T08:10:51.217 に答える
3

これは、参照によってstd::map::operator[]引数を取るためです。これにより、変数がODR 使用されます(C++11 標準の段落 3.2/3 を参照)。

要するに、コンパイラはオブジェクトへの参照をバインドする必要があるときにそのオブジェクトのアドレスを知る必要があり、そのため、そのオブジェクトを純粋な値のように扱って実行することが不可能になるという事実に要約されます。インライン化。

その場合、グローバル名前空間スコープで静的データ メンバーの定義を提供する必要があります。これにより、オブジェクトが占有するストレージの領域 (つまり、そのアドレス) をコンパイラが認識できるようになります。

template<int T>
const int A::Type;

C++11 標準のパラグラフ 9.4.2/3 によると:

不揮発性const静的データ メンバーが整数型または列挙型の場合、クラス定義でのその宣言は、代入式であるすべての初期化子句が 定数式であるブレースまたはイコール初期化子を指定できます (5.19)。 . [ ... ]プログラムで ODR 使用 (3.2) され、名前空間スコープ定義に初期化子が含まれていない場合、メンバーは名前空間スコープで定義されます

一方、プログラムの最初のバージョンでは、静的データ メンバーのTypeのみを使用していました。つまり、odr は使用されず、名前空間スコープでの定義は必要ありませんでした。

于 2013-05-24T08:10:54.630 に答える