我在使用 Apple LLVM 编译器(XCode 4.5.2 附带)时遇到了一个问题,同时使用 GCC 正确运行。比关于编译器问题的辩论更重要(尽管我认为 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*
还可以考虑for 的专业化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] 我已阅读有关模板专业化顺序的 Herb Shutter 建议。