4

次のコードは、clang と gcc の両方で拒否されます

template<typename T>
void f(T t)
{
    t.Dependent::f(); // clang accepts, gcc rejects
    t.operator Dependent*(); // both reject
}

struct Dependent
{
     void f();
};

struct A : Dependent
{
     operator Dependent*();
};

template void f<A>(A);

私が標準を読んだところ、両方の表現が受け入れられるべきであることが示唆されました。

どちらの場合もDependent、型名のみを指定できます。

どちらの場合も、名前Dependentは「オブジェクト式のクラスで検索」されますtt型に依存する式と同様に、テンプレートがインスタンス化されるまでルックアップを延期する必要があります。

足りないものはありますか?

編集: そのような名前が依存していないことが意図されている場合、この決定の根拠は何ですか? t.operator X::Dependent*名前空間または型名のt.X::Dependent::fような構造体の評価を延期する必要がなければ、実装者の生活が楽になることがわかります。Xこれが現在の文言の意図的な副作用なのか意図しない副作用なのかははっきりしません。

C++ Working Draft N3337 からの関連引用:

3.4.5 クラスメンバーアクセス [basic.lookup.classref]

クラス メンバー アクセスの id-expression が、クラス名または名前空間名::... の形式の修飾 ID である場合、. の後にクラス名または名前空間名が続きます。or -> 演算子は最初にオブジェクト式のクラスで検索され、見つかった場合はその名前が使用されます。それ以外の場合は、postfix-expression 全体のコンテキストで検索されます。[注: 3.4.3 を参照してください。これは :: の前の名前の検索について説明しています。これは型または名前空間の名前のみを検索します。—終わりのメモ]

id-expression が conversion-function-id の場合、最初にその conversion-type-id がオブジェクト式のクラスで検索され、見つかった場合はその名前が使用されます。それ以外の場合は、postfix-expression 全体のコンテキストで検索されます。これらの各ルックアップでは、型を表す名前、または特殊化が型であるテンプレートのみが考慮されます。

14.6.2 依存名 [temp.dep]

テンプレート内では、インスタンス化ごとに異なるセマンティクスを持つ一部の構成要素があります。このような構成は、テンプレート パラメーターに依存します。特に、型と式は、(テンプレート引数によって決定される) テンプレート パラメーターの型および/または値に依存する場合があり、これにより、特定の名前の名前検索のコンテキストが決定されます。式は、型依存 (テンプレート パラメーターの型) または値依存 (非型テンプレート パラメーターの値) の場合があります。

[...]

そのような名前はバインドされておらず、テンプレート定義のコンテキストとインスタンス化のポイントのコンテキストの両方で、テンプレートのインスタンス化のポイント (14.6.4.1) で検索されます。

14.6.2.1 依存型 [temp.dep.type]

名前は、不明な専門分野のメンバーです。

[...]

—クラス メンバー アクセス式(5.2.5)のメンバーを示す ID 式。

— オブジェクト式の型が現在のインスタンス化であり、現在のインスタンス化に少なくとも 1 つの依存基底クラスがあり、id-expression の名前検索で現在のインスタンス化のメンバーまたはその非依存基底クラスが見つからない。また

オブジェクト式の型は依存型であり、現在のインスタンス化ではありません

[...]

ある場合、型は従属です。

未知の専門分野のメンバー

4

1 に答える 1

1

1

これがあなたの最初のケースがどのように機能すると思うかt.Dependent::fです。まず、14.6.2.1p5 は「id-expression」ではなく「unqualified-id」とすべきだと思います (完全にはわかりません)。しかし、それとは別に、あなたの名前Dependent::fは実際には 2 つの名前から構成されています (標準では、ネストされたネストされた名前指定子の後にメンバー名が続くものはそれぞれ「修飾 ID」と呼ばれます。文法的には、これらは修飾 ID プロダクションではありません)。 . したがって、名前foo::bar::bazは修飾 ID ですが、他の「修飾 ID」も 1 つ含まれています)。

DependentDependent::f。前者は「クラスのメンバーアクセス式でメンバーを表すid式」ではないので、単純に に適用するルールを にも適用することはできませDependent::fDependent

Dependentしたがって、依存型ではなく、依存型内で検索する必要がありますが、定義時に見つける必要があります。個人的には、「修飾子が型に依存する修飾 ID を検索すると、名前検索は空の結果を生成する」という節が必要であり、これらの「名前検索をすぐに強制的に実行する」ことを適切に処理する必要があると思います。 . とにかく、結局のところ、あなたの最初のケースは見つからないことで形式が悪いと思いますDependent(3.4節は、14節の頭の上で、名前が実際に依存していると判断することはできません)。

2

あなたの他のケースではoperator Dependent、物事はより簡単です。あなたには再び 2 つの名前があり、Dependentoperator Dependent. ここでも、それが依存名であると言うものは何も見つかりませんでしDependentた (それが間違っているかどうかはわかりません。それは私を超えています)。

演算子関数名の名前検索比較 (たとえば、名前検索ハッシュ テーブルの等価関数) は、「同じ型で形成された変換関数 ID です」(3.8) です。これは、名前自体を形成するために (まだ名前の検索を行っていない!)、識別子の場合のように字句のスペルを指定するだけでなく、型 ID を指定する必要があることを意味します。Dependent.

依存する id-expression のルックアップt.operator Dependent*が遅れるということは、単にセマンティック型の比較が遅れることを意味します。これを試してみてください。うまくいくはずです

struct Dependent; // forward decl at global scope
t.operator Dependent*(); // in function template

あなたのフォローアップ

そのような名前が依存しないことが意図されている場合、この決定の論理的根拠は何ですか? t.operator X::Dependent* や tX::Dependent::f (X は名前空間または型名のいずれか) のような構成体の評価を延期する必要がなければ、実装者の作業が楽になることがわかります。 .

根拠はわかりませんが、あなたはすでに良い点を挙げていると思います。これは、修飾されていない名前を検索するときに、依存する基本クラスをスキップするルールに非常によく似ています。そして、そのケースに当てはまる理論的根拠は、このケースにも当てはまると思います。特に、プログラマーにとって関数テンプレートの推論が容易になります。

struct Dependent;

template<typename T>
void f(T t)
{
    t.Dependent::f();
    t.operator Dependent*();
}

コードは問題ないように見えますが、TたまたまDependentメンバがあると、突然Dependent別のバインドになります (最初にtのクラスを調べてから、周囲のスコープを調べるように指示されているため)。テンプレートルールに関する私の現在の理解では、上記は常に周囲のスコープを参照しているDependentため、上記のコードはその落とし穴に関して「安全」です。

于 2013-08-13T22:53:20.327 に答える