模板二进制运算符重载解析
Template binary operator overload resolution
我在使用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;
}
"有问题的行"在GCC上运行全局定义的operator*(matrix_t<T> const&, vectorN<U, N> const&)
,在LLVM上运行template <class U> matrix_t<T>::operator*(U const&) const
。因此,这就像matrix_t<T>::operator*(U const&)
正在捕获所有模板专用化查找一样。一个简单的"修复"是将全局operator*
移动到矩阵类中。
我最初认为这是特质类的一个问题,可能太复杂或错误了(SFINAE)。但是,即使简化特征或完全禁用它(如粘贴代码)也会产生错误。然后我认为这是一个顺序问题(就像Herb Shutter的文章中一样),但在matrix_t
声明和定义之间移动全局operator*
并不能改变事情。
这是个问题
当然,真正的问题是template <class U> matrix_t::operator*(U const&) const
过于笼统,但是:
- 这类问题是标准所涵盖的问题吗
- 类内部定义的运算符重载是否比全局定义的运算符过载具有优先级
- (更像是一个词汇问题)
operator*(matrix_t<T> const&, vectorN<U, N> const&)
的资格是什么?模板重载运算符专业化?这更像是一个模板专用化还是一个重载函数?它的基本定义是什么?由于它本质上是一个过载的运算符,我有点不知所措
[1] 我已经阅读了Herb Shutter关于模板专业化订单的建议。
类内部定义的运算符重载是否比全局定义的运算符过载具有优先级?
否。根据C++11标准第13.3.1/2段:
候选函数集可以包含要针对同一参数列表解析的成员函数和非成员函数。因此,在这个异构集合中,自变量和参数列表是可比较的,成员函数被认为有一个额外的参数,称为隐式对象参数,它表示调用成员函数的对象。为了解决重载问题,静态和非静态成员函数都有一个隐式对象参数,但构造函数没有。
此外,标准中没有提到成员职能优先于非成员职能,反之亦然。
这类问题是标准所涵盖的问题吗?
是的。之所以选择全局运算符(应该是!),是因为它是一个比类中定义的函数模板更专业的模板:经过模板参数推导,vectorN<U, N> const&
和matrix_t<T> const&
都可以匹配matrix_t<T> const& r
,但前者比后者更专业,因此,它是首选。
根据第13.3.3/1:段
给定这些定义,可行函数F1被定义为比另一个可行函数F2更好的函数,如果对于所有自变量i,ICSi(F1)不是比ICSi(F2)更差的转换序列,则:
[…]
--F1和F2是函数模板专业化,根据14.5.6.2中描述的偏序规则,F1的函数模板比F2的模板更专业。
因此:
因此,它就像matrix_t::operator*(U const&)正在捕获所有模板专用化查找
此行为属于错误。但是,请注意,这些不是专业化,而是重载。
最后:
operator * (matrix_t<T> const&, vectorN<U, N> const&)
的资格是什么?
我想你可以说这是一个(全局)运算符重载模板。这是而不是专门化,因为模板函数专门化有不同的语法。因此,没有它专门化的主要模板。它只是一个函数模板,一旦实例化,就会生成乘法运算符的重载。
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 重载运算符new[]的行为取决于析构函数
- 为什么将值返回函数传递给重载=运算符对运算符函数有效,而对其他运算符无效
- 在 myVector 类中重载运算符 + 时出错
- 为什么常量词在重载运算符中不与 ostream 对象一起使用<<?
- 如何在 cpp 中重载运算符 +=?
- C++ 如何重载 [] 运算符并进行函数调用
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- 重载运算符有地址吗?
- 如何迭代重载运算符 [] 的类?
- 重载运算符与添加问题
- 模板基类中的重载运算符
- 如何调用用于重载运算符"<<"的 friend 函数?
- 在 C++17 中的命名空间和子命名空间中重载运算符是不明确的
- 重载运算符<<采用谷歌 C++ 风格
- C++ 如何正确重载 + 运算符
- cout (<<) 重载运算符不打印减去的矩阵
- 如何在 c++ 中重载运算符 + 以便能够 whrite c_str = "smth" + c_str;
- 重载运算符*以获取对另一个类的实例的引用