2013-08-06

おそらくgccのバグ

以下の要素の参照は曖昧か - ここは匣

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