GCCで正しく実行しているときに、Apple LLVMコンパイラ(XCode 4.5.2に付属)を使用すると問題が発生しました。コンパイラの問題に関する議論よりも重要なことですが(GCCは正しいと思います)、これにより、オーバーロードされた演算子が関係する場合のテンプレートの特殊化の解決順序について疑問が生じます[1]。
template <class T> class matrix_t
乗算の結果タイプ(スカラー、行列、またはベクトルを使用)を定義する特性を持つ単純な行列クラスについて考えてみます。これは、次のコードのようになります。
template <class T, class U, class Enable = void>
struct matrix_multiplication_traits {
//typedef typename boost::numeric::conversion_traits<T,U>::supertype type;
typedef double type;
};
template <class T, int N>
struct vectorN {
std::vector<T> vect;
vectorN() : vect(N) { for(int i = 0; i < N; i++) vect[i] = i; }
};
template <class T>
class matrix_t {
std::vector<T> vec;
public:
matrix_t<T> operator*(matrix_t<T> const& r) const {
std::cout << "this_type operator*(this_type const& r) const" << std::endl;
return r;
}
template <class U>
matrix_t<typename matrix_multiplication_traits<T, U>::type>
operator*(U const &u) const {
std::cout << "different_type operator*(U const &u) const" << std::endl;
return matrix_t<typename matrix_multiplication_traits<T, U>::type>();
}
};
の専門化も検討operator*
してくださいvectorN
:
template <class T, class U, int N>
//vectorN<typename matrix_multiplication_traits<T,U>::type, N>
vectorN<double, N>
operator*(matrix_t<T> const&m, vectorN<U, N> const&v)
{
std::cout << "vectorN operator*(matrix, vectorN)" << std::endl;
//return vectorN<typename matrix_multiplication_traits<T,U>::type, N>();
return vectorN<double, N>();
}
簡単なテストプログラムを考えてみましょう。
int main(int argc, const char * argv[])
{
matrix_t<double> test;
vectorN<double, 10> my_vector;
test * my_vector; // problematic line
return 0;
}
「問題のある行」は、operator*(matrix_t<T> const&, vectorN<U, N> const&)
GCCとtemplate <class U> matrix_t<T>::operator*(U const&) const
LLVMでグローバルに定義されたものを実行します。つまり、matrix_t<T>::operator*(U const&)
はすべてのテンプレート特殊化ルックアップをキャッチしているようなものです。簡単な「修正」は、グローバルoperator*
をマトリックスクラスに移動することです。
私は最初、それが特性クラスの問題であり、複雑すぎるか誤っている可能性があると考えました(SFINAE)。ただし、特性を単純化するか、(貼り付けコードのように)完全に無効にしても、エラーが発生します。次に、それは順序の問題だと思いました(Herb Shutterの記事のように)が、宣言と定義operator*
の間でグローバルを移動matrix_t
しても状況は変わりません。
ここに質問があります
もちろん、本当の問題はそれtemplate <class U> matrix_t::operator*(U const&) const
があまりにも一般的であるということです、しかし:
- この種の問題は標準でカバーされているものですか?
- クラス内で定義された演算子のオーバーロードは、グローバルに定義された演算子のオーバーロードよりも優先されますか?
- (語彙の問題のように)資格は
operator*(matrix_t<T> const&, vectorN<U, N> const&)
何ですか?テンプレートオーバーロード演算子の特殊化?これはテンプレートの特殊化ですか、それともオーバーロードされた関数ですか?そのための基本的な定義は何ですか?それは本質的にオーバーロードされた演算子なので、私は少し迷っています。
[1]テンプレートの特殊化順序に関するハーブシャッターのアドバイスを読みました。