C++ 標準では、クラスの名前がクラス自身に「挿入」されます。これは、以前の C++ 規則からの変更です。それまでは、クラス名はクラス中に名前としては入っていませんでした。
ほとんどの場合、この微妙な変更が既存のプログラムに影響することはありません。しかし場合によっては、この変更のために、それまで有効だったプログラムが無効になったり、意味が変わったりすることがあります。たとえば、次の場合がそうです。
const int X = 5; class X { int i; public: X(int j = X) : // X のデフォルト値は何か? i(j) { } };
デフォルトパラメータ値としての X の意味を判定するために、コンパイラは、名前 X を見つけるまで現在のスコープを探し、次にその外のスコープを次々に探します。
古い C++ 規則では、クラス X の名前はこのクラススコープにはありません。そのため、ファイルスコープの整数名 X によってクラス名 X が隠されてしまいます。したがって、デフォルト値として 5 が返されます。
新しい C++ 規則では、クラス X の名前がこのクラス自身の中にあります。コンパイラはクラスの中で X を見つけ、エラーを出します。コンパイラが見つける X は型名であって、整数値ではないためです。
同じスコープで同じ名前の型とオブジェクトを持つことはプログラミング手法として望ましくないため、このエラーはめったに起こらないはずです。このようなエラーになる場合は、次のように、変数を適切なスコープで修飾してください。
X(int j = ::X)
次の例は、スコープに関する別の問題です (標準ライブラリのコードを改造したもの)。
template class<T> class iterator { ... }; template class<T> class list { public: class iterator { ... }; class const_iterator : public ::iterator<T> { public: const_iterator(const iterator&); // どの反復子か };
const_iterator のコンストラクタに対するパラメータの型は何でしょうか。古い C++ 規則では、コンパイラは、クラス const_iterator のスコープに iterator という名前がないため、次の外側のスコープであるクラス list<T> を探します。次のスコープにはメンバー型として iterator があるため、パラメータの型は list<T>::iterator です。
新しい C++ 規則では、クラスの名前がそれ自身のスコープに挿入されます。具体的には、基底クラスの名前がその基底クラスに挿入されます。コンパイラは、派生クラスのスコープで名前を探し、基底クラスの名前を見つけます。const_iterator コンストラクタに対するパラメータの型にはスコープ修飾子がないため、その名前が const_iterator 基底クラスの名前です。したがって、パラメータの型は、list<T>::iterator ではなく、大域的な ::iterator<T> です。
目的の結果を得るには、いずれかの名前を変更するか、次のようにスコープ修飾子を使用してください。
const_iterator(const list<T>::iterator&);