以下のコードは、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