13

次のコードを検討してください。

namespace foo {
  namespace bar {
    class foo {};
  }
  class baz {};
}
using namespace foo::bar;

::foo::baz mybaz;

このコードは有効ですか? それとも::foo曖昧ですか?または、 がないなど::fooの を参照します。class foo::foo::baz

コンパイラに関してgcc 6.1.1は、後者を考えるようです:

scope.cpp:9:8: error: ‘baz’ in ‘class foo::bar::foo’ does not name a type
 ::foo::baz mybaz;
        ^~~

一方gcc 5.3.1、、、clang 3.8.0インテル コンパイラ 16.0.3 では、警告やエラーは発生しません。

C++14 標準の 3.4.3.2.2 の下では、これは有効であり、あいまいではないはずですが、よくわかりません。

編集: さらに、foo::baz mybazclang のみがあいまいなエラーを報告します。

4

1 に答える 1

9

[namespace.qual] はかなり単純です。

1修飾 IDの入れ子になった名前指定子が名前空間を指定する場合 (入れ子になった名前指定子がである場合、つまり、グローバル名前空間を指定している場合を含む)、入れ子になった名前指定子の後に指定された名前が検索されます。名前空間のスコープ内。template-idtemplate-argument内の名前は、postfix-expression全体が発生するコンテキストで検索されます。::

2 名前空間Xと namemの場合、名前空間で修飾されたルックアップ セット S( , )はX次のmように定義されます。S 0 ( , ) が空でない場合、S( , ) は S 0 ( , ) です。それ以外の場合、S( , ) は、 inの using ディレクティブによって指定されたすべての名前空間 N iとそのインライン名前空間セットの S(N i , ) の和集合です。XmmXXXmXmXmXmmX

3指定X::m(Xはユーザー宣言の名前空間) または指定::m(Xはグローバル名前空間) で、S( X, m) が空のセットの場合、プログラムの形式は正しくありません。それ以外の場合、S( X, m) がちょうど 1 つのメンバーを持っている場合、または参照のコンテキストがusing 宣言(7.3.3) である場合、S( X, m) は の宣言の必須セットですm。それ以外の場合、 S( , )mから一意の宣言を選択できるように使用されていない場合、プログラムの形式が正しくありません。Xm

::fooは、nested-name-specifierを持つ修飾 IDであるため、名前はグローバル スコープで検索されます。 ::foo

S 0 ( ::, ) には、名前空間に他の宣言がなく、インライン名前空間がないfooため、単一の宣言が含まれています。S 0 ( , ) は空ではないので、S ( , ) は S 0 ( , ) です。( S 0 ( , ) は空ではないため、 using ディレクティブによって指定された名前空間は調べられないことに注意してください。)namespace foofoo::foo::foo::foo::foo

S( ::, foo) には要素が 1 つしかないため、その宣言は に使用され::fooます。

::foo::bazしたがって、名前は名前空間を指定するネストされた名前指定子を持つ修飾 IDです。ここでもin namespaceの宣言が 1 つあるため、名前は宣言を参照します。 ::foobaz::foo::foo::bazclass baz

GCC 6+ で観察される動作は、実際にはGCC PR 71173として提出されたバグです。

編集:foo::bazグローバルスコープで表示されるときのルックアップ、ラ:

foo::baz bang;

foo最初に非修飾名として を検索する必要があります。[basic.lookup.qual]/1 は、このルックアップが「特殊化が型である名前空間、型、およびテンプレート」のみを参照することを示しています。[basic.scope.namespace]/1 は、名前空間のメンバーとそのスコープについて教えてくれます。

namespace-definitionの宣言領域は、そのnamespace-bodyです。名前空間本体で宣言されたエンティティは、名前空間のメンバーと呼ばれ、これらの宣言によって名前空間の宣言領域に導入された名前は、名前空間のメンバー名と呼ばれます。名前空間のメンバー名には、名前空間のスコープがあります。その潜在的なスコープには、名前の宣言時点からの名前空間が含まれます。また、メンバーの名前空間を指定する各using ディレクティブについて、メンバーの潜在的なスコープには、メンバーの宣言ポイントに続くusing ディレクティブの潜在的なスコープの部分が含まれます。

[basic.lookup.unqual]/4 は、 の宣言namespace fooが可視であることを示しています。

関数、クラス、またはユーザーが宣言した名前空間の外側で、グローバル スコープで使用される名前は、グローバル スコープで使用する前に宣言する必要があります。

そしてパラ2はそれclass fooがここにも見られると言います:

using-directiveによって指定された名前空間からの宣言は、 using- directiveを囲む名前空間で可視になります。7.3.4 を参照。3.4.1 で説明されている非修飾名ルックアップ規則のために、using ディレクティブによって指定された名前空間からの宣言は、その囲んでいる名前空間のメンバーと見なされます。

異なる名前空間からの異なるエンティティの 2 つの宣言fooが見つかったため、[namespace.udir]/6 は、の使用fooが不適切であることを示しています。

名前検索で 2 つの異なる名前空間で名前の宣言が検出され、宣言が同じエンティティを宣言せず、関数も宣言されていない場合、その名前の使用は不適切です。

foo::bazに到達する前に、ダイの名前を検索しbazます。

于 2016-05-18T14:50:27.003 に答える