1. ホーム
  2. c++

[解決済み] 静的な const int への未定義の参照

2023-05-29 01:35:13

質問

今日、興味深い問題にぶつかりました。 この簡単な例について考えてみましょう。

template <typename T>
void foo(const T & a) { /* code */ }

// This would also fail
// void foo(const int & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

int main()
{
   Bar b;
   b.func();
}

コンパイル時にエラーが出ます。

Undefined reference to 'Bar::kConst'

さて、これは間違いなく static const int がどこにも定義されていないからだと思います。私の理解では、コンパイラはコンパイル時に置換を行うことができ、定義を必要としないはずなので、これは意図的なものです。 しかし、この関数は const int & パラメータを取るので、置換を行わず、参照を優先しているようです。 次のように変更することで、この問題を解決することができます。

foo(static_cast<int>(kConst));

これは今、コンパイラが一時的にintを作り、その参照を渡すことを強制していると思いますが、これはコンパイル時に正常に行うことができます。

これは意図的なものなのでしょうか、それとも、このケースを処理できるように gcc に期待しすぎているのでしょうか。 あるいは、これは何らかの理由でやってはいけないことなのでしょうか。

どのように解決するのですか?

9.4.2/4では意図的なものだそうです。

静的データメンバが const integral 型または const enumeration 型である場合。 クラス定義でその宣言を指定することができます。 を指定することができます。 定数イニシャライザを指定することができます。 定数イニシャライザを指定することができます (5.19) その場合、そのメンバは に現れることができる。この場合 メンバは,依然として,名前空間スコープで定義されなければならない。 で使用される場合,そのメンバは,依然として名前空間スコープで定義されなければならない。 プログラム

静的データメンバをconst参照で渡すと、"use"する、3.2/2:

式は潜在的に評価される 式は評価される可能性がありますが、積分 定数式が必要な場合(5.19参照)、または 5.19参照)、sizeof演算子のオペランド(5.3.3)、またはtypeid演算子のオペランドであって のオペランドであり,かつ式 の lvalue を指定しない。 多相クラス型(5.2.8)のl値を指定しない。オブジェクト オブジェクトまたは非オーバーロード関数は その名前が評価される可能性のある式に現れる場合、オブジェクトまたはオーバーロードされていない関数が使用されます。 評価される可能性のある式にその名前が現れる場合,オブジェクト又はオーバーロードされていない関数が使用される。

つまり、実際には値で渡すときにも "use"を使うか、あるいは static_cast . ただ、GCCは一方のケースでは見逃してくれますが、もう一方のケースでは見逃してくれません。

[編集: gcc は C++0x ドラフトからのルールを適用しています: "潜在的に評価される式として名前が現れる変数またはオーバーロードされていない関数は、それが定数式に現れるための要件 (5.19) を満たすオブジェクトで、lvalue-rvalue 変換 (4.1) が直ちに適用されない限り、odor 使用となります)"。静的キャストはlvalue-rvalue変換を即座に行うので、C++0xでは"used"ではありません]。

const参照の実用的な問題点は foo がその引数のアドレスを取得し、例えばグローバルに格納されている別の呼び出しからの引数のアドレスと比較する権利の範囲内にあることです。静的データメンバは一意なオブジェクトなので、もしあなたが foo(kConst) を呼び出すと、渡されるオブジェクトのアドレスはそれぞれのケースで同じでなければならないことを意味します。AFAIK GCCは、オブジェクトが1つの(そして唯一の)TUで定義されていない限り、それを手配することができません。

OK、ではこの場合 foo はテンプレートであり、したがってその定義はすべての TU で見ることができるので、おそらくコンパイラは理論的にはそのアドレスで何かをするリスクを除外することができます。しかし、一般的には、存在しないオブジェクトのアドレスや参照を取るべきではありません;-)。