3

なぜこれが機能するのですか:

template <typename A>
struct S {
    A a;
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
};

しかし、これはそうではありません (aそしてf場所を入れ替えました):

template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
    A a;
};

aそのスコープ(decltype内)で宣言されていないと言っていますが、明示的に追加this->すると機能します。

4

3 に答える 3

5
template <typename A>
struct S {
    A a;
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
};

これが機能するのは、末尾の戻り値の型内で、周囲のクラスのメンバーが表示されるためです。すべてのメンバーではなく、その前に宣言されているメンバーのみです (末尾の戻り値の型では、関数本体とは対照的に、クラスは完全であるとは見なされません)。それで、ここで何が行われます:

  • aテンプレートを使用しているため、が依存しているかどうかを確認するためにルックアップが行われます。aは より前に宣言されているためfaはクラス メンバを参照していることがわかりました。
  • aC++ のテンプレート規則により、が現在のインスタンス化のメンバーを参照していることがわかります。これは、周囲のテンプレートのインスタンス化のメンバーであるためです。C++ では、この概念は主に名前が依存しているかどうかを判断するために使用されます。名前が周囲のテンプレートのメンバーを参照することがわかっている場合、コンパイラはテンプレートのコードを既に知っているため、インスタンス化するときに必ずしも検索する必要はありません。 (これは、インスタンス化されたクラス型の基礎として使用されます!)。検討:

    template<typename T>
    struct A {
      typedef int type;
      void f() {
        type x;
        A<T>::type y;
      }
    };
    

C++03 では、2 行目の宣言yはエラーになります。これA<T>::typeは、 が従属名であり、そのtypename前に が必要だったためです。最初の行のみが受け入れられました。C++11 では、この矛盾は修正され、両方の型名は非依存であり、typename. typedef をtypedef T type;両方の宣言に変更するxy、依存型が使用されますが、どちらも必要ありませんtypename。これは、現在のインスタンス化のメンバーにまだ名前を付けており、コンパイラーが型に名前を付けていることを認識しているためです。

  • 現在のインスタンスa化のメンバーも同様です。ただし、それを宣言するために使用される型 ( A) が依存しているため、依存しています。ただし、これはコードでは問題になりません。依存しているかどうかに関係なく、a見つかってコードが有効になります。

template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
    A a;
};

このコードでaは、 が依存しているかどうか、および/または現在のインスタンス化のメンバーであるかどうかを確認するために再度検索されます。ただし、末尾の戻り値の型の後に宣言されたメンバーは表示されないことを上記で学習したため、 の宣言が見つかりませんa。C++ では、「現在のインスタンス化のメンバー」という概念の他に、別の概念があります。

  • 未知の専門分野のメンバー。この概念は、名前がテンプレート パラメーターに依存するクラスのメンバーを代わりに参照する場合を参照するために使用されます。にアクセスB::aした場合、は未知aの特殊化のメンバーになります。これは、インスタンス化時に が置換されたときにどの宣言が表示されるかがわからないためです。B

  • 現在のメンバーでも、未知の専門分野のメンバーでもありません。これは、他のすべての名前の場合です。インスタンス化が発生したときに がインスタンス化のメンバーになることは決してないことがわかっているため、あなたのケースはここに当てはまります( の後に宣言されているため、名前の検索では が見つからないことに注意してください)。aaf

はどのルールにも依存しないためa、宣言が見つからなかったルックアップはbindingです。つまり、インスタンス化時に宣言を見つけることができる他のルックアップはありません。依存しない名前は、テンプレート定義時に検索されます。現在、GCC は正当にエラーを返します (ただし、いつものように、不適切な形式のテンプレートをすぐに診断する必要はないことに注意してください)。


template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(this->a.f(b))
    {
    }
    A a;
};

この場合、追加thisして GCC が受け入れました。a再び続く名前this->は、それが現在のインスタンス化のメンバーであるかどうかを確認するためのルックアップです。しかし、ここでも、末尾の戻り値の型のメンバーの可視性のために、宣言が見つかりません。したがって、名前は現在のインスタンス化のメンバーではないと見なされます。インスタンス化時に、一致する可能S性のある追加のメンバーを持つ方法がないため (テンプレート パラメーターに依存するa基本クラスはありません)、名前は未知の特殊化のメンバーでもありません。S

繰り返しますが、C++ にはthis->a従属させる規則がありません。ただし、 を使用するthis->ため、名前はインスタンス化されたときにのメンバーを参照する必要があります。Sしたがって、C++標準は言う

同様に、オブジェクト式の型が現在のインスタンス化であるクラス メンバー アクセス式の id-expression が、現在のインスタンス化のメンバーまたは未知の特殊化のメンバーを参照していない場合、プログラムの形式が正しくありません。メンバー アクセス式を含むテンプレートがインスタンス化されていない場合。診断は必要ありません。

繰り返しますが、このコードには診断は必要ありません (実際には GCC はそれを提供しません)。aメンバ アクセス式の id-expressionthis->aは C++03 に依存していました。これは、その標準の規則が C++11 ほど精巧で微調整されていないためです。decltypeちょっとの間、C++03 にと末尾の戻り値の型があると想像してみましょう。これはどういう意味ですか?

  • this->a依存するため、インスタンス化までルックアップが遅れていた
  • たとえば、 のインスタンス化時のルックアップS<SomeClass>は失敗します。これthis->aは、インスタンス化時に が見つからないためです (既に述べたように、末尾の戻り値の型には後で宣言されたメンバーが表示されません)。

したがって、C++11 によるそのコードの早期拒否は、適切で有用です。

于 2012-12-22T17:42:15.487 に答える
2

メンバー関数の本体は、クラスの後に定義されたかのようにコンパイルされます。したがって、クラスで宣言されたものはすべて、その時点でスコープ内にあります。

ただし、関数の宣言は依然としてクラス宣言内にあり、その前にある名前のみを確認できます。

template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b)); // error - a is not visible here

    A a;
};

template <typename A>
template <typename B>
    auto S<A>::f(B b) ->
        decltype(a.f(b))
    {
        return a.f(b);   // a is visible here
    }
于 2012-12-21T17:26:22.493 に答える