以下のコードは、gccとclangで解釈が異なる。
namespace NS { template < int N > struct NS { constexpr static int value = N ; } ; } int main() { using namespace NS ; NS<0>::value ; // gcc says a name 'NS' is ambiguous. // clang says 'NS' is class template name NS::NS ; }
gccは名前NSを曖昧だとするが、clangは曖昧なくクラステンプレート名NS::NSだとする。
規格に照らし合わせると、おそらくclangの実装が正しく、gccの実装はバグであると思われる。3.4の冒頭に書いてあるように、
Name Lookupは、文法がその文脈上許す名前のみに行われる[3.4 Name lookup]。
たとえば、以下のようなコードを考える。
struct X { } ; X X ; // #1、OK X Y ; // #2、エラー、曖昧 struct X Z ; // #3、OK int main() { struct X X = ::X ; // #4、OK }
#1の宣言文のName lookupに曖昧性はない。なぜならば、type specifierにXを使う時点では、まだ変数名Xは宣言されていないからだ。ただし、ある文脈で同じ名前のクラス名と変数名が同時に見える場合、のちの宣言文が少々厄介なことになる。
#2の名前Xは、Name lookupが曖昧となる。なぜならば、C++の宣言文にしっかりとした区別可能な文法ではないので、この文脈上では、変数名も現れることを許してしまう。そのため、name lookupはクラス名Xと変数名Xを見つけ出してしまい、曖昧となる。これを解決するには、Elaborated type specifierを使い、type specifierに現れるXはクラス名であると明示的に記述しなければならない。
#3には、曖昧性はない。なぜならば、この文脈では、Elaborated type specifierにより、文法の制約でXはクラス名でなければならないからだ。ここでは変数名は、文法上使えない。したがって、name lookupはクラス名しか発見せず、曖昧ではなくなる。
#4には、曖昧性はない。まず、Elaborated type specifierにより、最初のXは、文法上クラス名でなければならない。そのため、曖昧性はない。次のXは、関数のブロックスコープ内で名前を宣言している。これは外側のグローバル名前空間スコープの名前Xを隠すが、これ自体には問題はない。内側のスコープが外側のスコープの名前を隠すというのは、C++ではよく使われることだ。最後の初期化式に使われているXは、スコープ解決演算子により、グローバル名前空間の名前であることを明示的に指定している。そのため、文法上、この宣言文の中で宣言したばかりの関数のブロックスコープの変数名はlookupされない。また、文法上、クラス名をここに使うことはできないので、グローバル名前空間スコープの変数名Xだけが発見される。そのため、ここでも曖昧性はない。
このように、C++では、name lookupは、その文脈で、文法上許される名前からしか探されない。
これを元に最初のコードを読む。using directiveにより、main関数のブロックスコープ内では、'NS'という名前に対して、名前空間名NSと、クラステンプレート名NS::NSが同時に存在する。ただし、NS<0>::valueという文脈では、名前空間名は、文法上使えない。C++には名前空間テンプレートはないし、名前空間には比較演算子を適用できないからだ。
そのため、これはgccのバグであろう。
No comments:
Post a Comment