質問
メンバーのポインターに関して問題があります。次のコードは、Oracle Solaris Studio 12.2のCCとCygwin GCC 4.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;
}
次の最後の行で、上記の両方のコンパイラがマッチを見つけることができません 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));
}
};
私の質問:どの動作が正しいですか?どうやらVC ++は、からの暗黙のアップキャストを行っています int A::*
に int B::*
メンバー関数テンプレートへの呼び出しを満たすために、他の2人のコンパイラが同じことをすることを検討すべきではありませんか?
解決
からの変換 int A::*
に int B::*
許可されており、それは問題ではありません。問題は、テンプレート引数を提供する次のプログラムを試すかどうかを確認できるように、テンプレート引数控除にあります <int>
にとって B::foo
コンパイル、および非会員関数 foo2
同じエラーが生成されます B::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が上記のように変換された後)。ただし、違いを可能にする3つのケースがあります。
- 元のPが参照タイプの場合、推定されたA(つまり、参照によって言及されるタイプ)は、AよりもCV資格を取得できます。
- Aは、資格変換(CONV.QUAL)を介して推定されたAに変換できる別のポインターまたはメンバータイプへのポインターにすることができます。
- Pがクラスであり、PがフォームテンプレートIDを持っている場合、Aは推定Aの派生クラスになります。同様に、PがTemplate-IDのクラスへのポインターである場合、Aは推定されたAによって指された派生クラス
ここで「P」はテンプレート関数の引数タイプです。 M B::*p
, 、テンプレートタイプパラメーター M
決定されるべきです。 「A」は実際の議論のタイプです。 int A::*
. 。 PとAは確かにリファレンスやクラスではなく、これが機能するために必要なメンバーからメンバーへの変換のようなものは、資格変換ではありません(これは、const/volatile操作のみを説明しています。 X*
に const X*
また int X::*
に const int X::*
).
したがって、テンプレートの引数を推定することはできません。 <int>
コードへの明示的なテンプレートパラメーター。