4

メンバーポインタに関して問題があります。次のコードは、Oracle SolarisStudio12.2のCCとcygwinGCC4.3.4の両方を使用してコンパイルできませんが、Microsoft Visual C++2010では機能します。

struct A {
  int x;
};

struct B : public A {
};

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

int main(int, char *[]) {
    Bar<B> bbar;
    bbar.foo(&B::x);
    return 0;
}

最後から2番目の行で、上記の両方のコンパイラはに一致するものを見つけることができませんBar<B>::foo(int A::*)。式のタイプ&B::xが実際に次のint A::*とおりであることを確認するための簡単なテストを作成しました。

// ...

static void foo(int A::*p) {
  std::cout << "A" << std::endl;
}

static void foo(int B::*p) {
  std::cout << "B" << std::endl;
}

int main(int, char *[]) {
    foo(&B::x);  // prints "A", even on MS VC++ 2010 
    return 0;
}

次の回避策はGCC(Oracle CCではまだテストされていません)では機能しますが、あいまいさのためにVC++では失敗します。

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
  template<typename M, typename _T_base> inline void foo(M _T_base::*p) {
      foo(static_cast<M T::*>(p));
  }
};

私の質問:どの動作が正しいですか?int A::*どうやらVC++は、メンバー関数テンプレートへの呼び出しを満たすために、からへの暗黙のアップキャストを実行しint B::*ますが、他の2つのコンパイラは同じことを検討するべきではありませんか?

4

1 に答える 1

8

int A::*からへの変換int B::*は許可されており、それは問題ではありません。<int>問題はテンプレート引数推定にあります。テンプレート引数を提供してコンパイルする次のプログラムと、以前と同じエラーを生成するB::foo非メンバー関数を試してみるとわかります。foo2B::foo

struct A {
  int x;
};

struct B : public A {
};

template <typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

template<typename M> void foo2(M B::*p);

int main(int, char*[]) {
  Bar<B> bbar;
  bbar.foo<int>(&B::x);
  foo2(&B::x); // error, but foo2<int>(&B::x) would work.
  return 0;
}

<int>この状況は、コンパイラが独自にテンプレート引数を推測することになっているケースではカバーされていないと思います。14.8.2.1p3:

一般に、演繹プロセスは、演繹された A を A と同一にするテンプレート引数値を見つけようとします (型 A が上記のように変換された後)。ただし、違いが認められる 3 つのケースがあります。

  • 元の P が参照型である場合、推定された A (つまり、参照によって参照される型) は、A よりも cv 修飾されている可能性があります。
  • A は、修飾変換 (conv.qual) を介して推定 A に変換できる別のポインターまたはメンバー型へのポインターにすることができます。
  • P がクラスであり、P の形式が template-id である場合、A は推定された A の派生クラスである可能性があります。同様に、P が形式 template-id のクラスへのポインタである場合、A は次へのポインタです。推定された A が指す派生クラス。

ここで、「P」はテンプレート関数の引数の型です: M B::*p、ここでテンプレートの型パラメーターMが決定されます。"A" は実引数の型です: int A::*. P と A は確かに参照でもクラスでもなく、これが機能するために必要なポインターからメンバーへの変換の種類は修飾変換ではありません (これはX*toconst X*またはint X::*toのような const/volatile 操作のみを記述しconst int X::*ます)。

したがって、テンプレート引数を推測することはできず、<int>明示的なテンプレート パラメーターをコードに追加する必要があります。

于 2010-10-04T23:11:48.150 に答える