我在使用Apple LLVM编译器(XCode 4.5.2附带)时遇到了问题,同时正确运行GCC.更重要的是比关于编译器问题的辩论(我认为GCC是正确的),这引发了关于重载运算符时模板特化的解决顺序的问题[1].
考虑一个简单的矩阵类模板< 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>();
}
};
还要考虑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;
}
“有问题的行”在GCC和模板 上运行全局定义的运算符*(matrix_t< T> const&,vectorN< U,N> const&). LLVM上的matrix_t< T> :: operator *(U const&)const.因此,就像matrix_t< T> :: operator *(U const&)正在捕获所有模板特化查找.一个简单的“修复”是将全局运算符*移动到矩阵类中.
我首先认为这是特质课中的一个问题,可能过于复杂或错误(SFINAE).但即使简化特征或完全禁用它(如在粘贴代码中)也会产生错误.然后我认为这是一个订单问题(比如Herb Shutter的文章),但是在matrix_t声明和定义之间移动全局运算符*并没有改变.
这是个问题
当然,真正的问题是模板 matrix_t :: operator *(U const&)const太笼统了,但是:
>标准所涉及的这类问题是什么?
>在类中定义的运算符重载是否优先于全局定义的运算符重载?
>(更像词汇问题)运算符*的限定是什么(matrix_t< T> const&,vectorN< U,N> const&)?模板过载运算符专业化?这更像是模板专业化还是重载功能?那个基本定义是什么?由于它本质上是一个重载的运算符,我有点迷失.
[1]我已经阅读了关于模板专业化顺序的Herb Shutter建议.
最佳答案
does an operator overload defined inside a class have priority over an operator overload defined globally?
根据C 11标准第13.3.1 / 2段的规定:
The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called. For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
此外,标准成员函数中没有提到优先于非成员函数,反之亦然.
is this kind of problem something covered by the standard?
是.选择全局运算符(应该是!)的原因是它是比类中定义的函数模板更专业的模板:在模板参数推导之后,vectorN< U,N>常量和放大器;和matrix_t< T>常量和放大器;可以使matrix_t< T>匹配常量和放大器; r,但前者比后者更专业,因此,它是优选的.
根据第13.3.3 / 1段:
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then:
[…]
— F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.
因此:
So it is like the matrix_t::operator*(U const&) is catching all template specialization lookups
此行为符合错误.但请注意,这些不是专业化,而是重载.
最后:
what is the qualification of
operator * (matrix_t<T> const&, vectorN<U, N> const&)
?
我想你可以说它是一个(全局)运算符重载模板.它不是专门化,因为模板函数特化具有不同的语法.因此,没有专门的主要模板.它只是一个函数模板,一旦实例化,就会生成乘法运算符的重载.