9

このコードをg++4.6および4.8でコンパイルするとエラーが発生しました。g++4.2および4.4は問題ありません。それはバグですか、それとも新しい言語機能ですか?

template <typename T>
struct A { typedef typename T::value_type type; };

template <typename U>
struct B
{
  void bar () { }
  void foo ()
  {
    // OK
    this->bar ();

    // OK
    (*this).bar ();

    // Error in g++ 4.6-4.8 
    // leads to full instantiating of template arg "U"
    (&*this)->bar ();
  }
};

int main ()
{
  B< A<void> > b;
  b.foo ();
  return 0;
}

g ++ inst.cc

inst.cc: In instantiation of ‘struct A<void>’:
inst.cc:20:5:   required from ‘void B<U>::foo() [with U = A<void>]’
inst.cc:27:10:   required from here
inst.cc:3:34: error: ‘void’ is not a class, struct, or union type
   typedef typename T::value_type type;
                                  ^

アップデート1:Aはインスタンス化できません。

問題は、コンパイラが「(&* this)-> bar()」行でインスタンス化しようとするが、「this-> bar()」または「(* this).bar()」行ではインスタンス化しないのはなぜですか?

アップデート2

を使用しようとすると実際にエラーが発生したため、推奨される回避策addressof (object)は機能しませんstd::bind (&B::bar, this)。もちろん、実際のコードははるかに複雑で、bindスタンドアロンでは使用されませんでしたが、問題は単純なstd::bind式に起因していました。

書き直したり、作り直したりしたくなかったので、 CRTPstd::bindを使用して機能させる必要がありました。

#include <tr1/functional>
template <typename T>
struct A { typedef typename T::value_type type; };

template <typename Derived, typename U>
struct B
{
  Derived* derived (void) { return static_cast<Derived*>(this); }

  void bar () { }
  void foo ()
  {
    // error with recent compiler.
    // std::tr1::bind (&B::bar, this) ();

    // now ok
    std::tr1::bind (&Derived::bar, derived ()) ();
  }
};

struct C: B<C, A<void> >
{
};

int main ()
{
  C c;
  c.foo ();
  return 0;
}

ただし、このようなエラーと回避策は完全に非論理的です。

4

1 に答える 1

11

分析/説明:

あなたが見ているのは浅いインスタンス化であり、完全ではありません(証拠については以下を参照してください)。

ここではADLが原因です。

仮説IIここでADL関連のことを疑っています(クラスは静的なフリー関数(フレンド)をインラインで宣言できます。おそらく、コンパイラは、演算子のオーバーロードが宣言されていることを確認するために、クラステンプレート全体をインスタンス化する必要があります(過負荷解決を行うため)。

標準はここで私をバックアップします:§3.4.2(n3337のp46):

² [snip]名前空間とクラスのセットは、関数の引数のタイプ(および任意のテンプレートテンプレート引数の名前空間)によって完全に決定されます。[snip]名前空間とクラスのセットは次の方法で決定されます。

  • [をちょきちょきと切る]

  • Tがクラスタイプ(ユニオンを含む)の場合、関連するクラスは次のとおりです。クラス自体。メンバーであるクラス(ある場合)。およびその直接および間接の基本クラス。関連する名前空間は、関連するクラスがメンバーである名前空間です。さらに、Tがクラステンプレートの特殊化である場合、それに関連付けられた名前空間とクラスには、次のものも含まれます。テンプレートタイプパラメーター(テンプレートテンプレートパラメーターを除く)に提供されるテンプレート引数のタイプに関連付けられた名前空間とクラス。テンプレートテンプレート引数がメンバーである名前空間。テンプレートテンプレートの引数として使用されるメンバーテンプレートがメンバーであるクラス。

太字のフレーズにはclass A<void>、ADLのルックアップ名前空間が含まれています。

回避策:

あなたの状況std::addressof(b)では代わりに使用することができ&b、それは動作します。

デモンストレーション:

http://liveworkspace.org/code/4f85a06598eebe1d8060112be36f4a29を参照してください

注:(unqualified-id)トリックは標準の§3.4.2で定義されています)

#include <vector>
#include <iostream>

struct Base {};

template <typename U> struct B : Base { };

template <typename T> struct A {
    typedef typename T::value_type type;
    friend void freefunction(B<A>&) { std::cout << "ADL was here!\n"; }
};

void freefunction(Base& /*acceptAll*/) {}

int main ()
{
    B< A<std::vector<int> > >  a;
    B< A<void> >               b;

    // surrounding with parens prevents ADL:
    (freefunction)(a);
    (freefunction)(b); // selects ::freefunction(Base&)

    freefunction(a);   // ADL selects friend inline freefunction(B< A<std::vector<int> > >&)
  //freefunction(b);   // ADL fails: template arg cannot be (shallow) instantiated
}

プリント

ADL was here!

また、テンプレート引数(A<void>)が浅くインスタンス化されるだけであることを確認できます。不正な形式のtypedefをメンバー関数に移動すると、問題が解消されます。

template <typename T> struct A {
    void uninstantiated() {
        typedef typename T::value_type type;
    }
    friend void freefunction(B<A>&) { std::cout << "ADL was here!\n"; }
};

出力(http://liveworkspace.org/code/a15c933293281d0926e8b1ff39180079

ADL was here!
ADL was here!

歴史:

  1. operator&問題があることに気づきましたが、std::addressof()大丈夫でした!
  2. (オーバーロードされた)演算子を使用すると、この動作がトリガーされるようです。

これは私の「仮説II」に私を導きました(上記を参照)

于 2012-10-16T19:01:52.463 に答える