3

C++ 標準では (9.4.2 節 4 節)、整数型または列挙型の静的メンバー変数はクラス内で初期化子を提供できるが、これにはクラス外 (コンパイル ユニット内) でそのメンバーの定義が必要であることがわかっています。 )。つまり、次のようなことをする必要があります。

class A
{
public:
    static const int X = 10;
};

// this is required by the standard
const int A::X;

一部のコンパイラでは、クラス外の定義がなくても問題なく処理できることがわかりました (他の場所でも述べられています)。これは、OS X の gcc 4.2.1 で動作します。

#include <iostream>    

class A
{
public:
    static const int X = 10;
};

int main(int argc, char** argv)
{
    std::cout << A::X << std::endl;
    return 0;
}

私は最近、誰かがこれを行ったというバグに遭遇しましたが、彼らはテンプレート化された関数内でメンバー変数を使用していました (std::max正確には)、未定義のシンボル A::XIe について不平を言い、コンパイルされませんでした。これは機能しません:

#include <iostream> 
#include <algorithm>   

class A
{
public:
    static const int X = 10;
};

int main(int argc, char** argv)
{
    std::cout << std::max(1, A::X) << std::endl;
    return 0;
}

クラス外の定義を追加し直すと、機能します。

これは一種の学術的な質問ですが、なぜこのようなことが起こるのか知りたいです。特に、静的メンバー変数を静的関数 ( become 、 become ) に置き換えると、クラス外の定義なしでコンパイルされるという事実に関連してstatic const int X = 10;。テンプレートに言及する理由は、がテンプレート化されており、他のテンプレート化された関数が同じ動作を再現するためです。テンプレートに特に関連しているわけではないかもしれませんが、テンプレートがその動作を引き起こす理由を理解したいと思います。これは、テンプレートと静的メンバーがコンパイル/実装される方法に関係していると思いますか?static int X()A::XA::X()std::max

PS - 最小限のコードをgithubに投稿しました

4

2 に答える 2

3

定義なしでコンパイルされます。

静的メンバー変数がodr-usedの場合、リンク時に定義が必要です。(コンパイラーが参照されるたびに実際の値を置き換えることができれば、 odr-usedにはなりません。一方、そのアドレスを取得すると、必ずodr-usedになります)

これは完全なルールです(セクション9.4.2 [class.static.data]):

不揮発性const staticデータメンバーが整数型または列挙型の場合、クラス定義での宣言で、代入式であるすべての初期化子句 が定数式である中括弧または等しい初期化子を指定できます。リテラル型のデータメンバーは、指定子を使用してクラス定義で宣言できます。その場合、その宣言は、代入式であるすべての初期化子句が定数式である中括弧または等しい初期化子を指定するものとします。[注:どちらの場合も、メンバーは定数式で表示される場合があります。—エンドノート]staticconstexprメンバーがプログラムでodr-usedであり、名前空間スコープ定義に初期化子が含まれていない場合でも、メンバーは名前空間スコープで定義されます。

およびセクション3.2から[basic.def.odr]

名前が潜在的に評価される式として表示される変数は、定数式に表示されるための要件を満たし、左辺値から右辺値への変換がすぐに適用されるオブジェクトでない限り、odrで使用されます。

定数式に表示されるための要件が​​満たされているため、すべてが左辺値として使用されるか右辺値として使用されるかによって異なります。

std::max左辺値参照を取得するため、左辺値から右辺値への即時変換はありません。


テンプレートとの唯一の相互作用は、複数の同等の定義が存在する可能性があり、リンカがいずれかを選択することです。あなたの場合、テンプレートがないので、複数の定義は「シンボル多重定義」タイプのエラーを生成します。

定義を指定するのを忘れると、どちらの場合(テンプレートクラスのメンバーと通常のクラスのメンバー)でも同じエラーが発生します:「未定義のシンボル」。

于 2012-07-04T23:06:50.890 に答える
2

最初の例を見てみましょう:

std::cout << A::X << std::endl;

A::Xは 型なのでconst int、これは を呼び出しますcout.operator<<(int value)。この呼び出しint valueではvalueが使用されることに注意してください。コンパイラは定数の折りたたみを実行し、を値に置き換えるだけだと思いA::Xます。ただし、C++03 ではこれを行う必要はありません。ただし、C++11 ではルールが変更され、これは次の引用で要求されるようになりました: unless it is an object that satisfies the requirements for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied.(Ben Voigt が述べたように)。

の定義を見てみましょうstd::max(1, A::X)

template< class T >
const T& max( const T& a, const T& b ); 

関数はそのstd::max引数を参照A::Xによって受け取ります。つまり、呼び出しを完了するには、のアドレスを知る必要があります。A::x定義が必要なのアドレスを知っている必要があります。

于 2012-07-04T23:12:33.903 に答える