模板二进制运算符重载解析

Template binary operator overload resolution

本文关键字:重载 运算符 二进制      更新时间:2023-10-16

我在使用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&)的资格是什么?

我想你可以说这是一个(全局)运算符重载模板。这是而不是专门化,因为模板函数专门化有不同的语法。因此,没有它专门化的主要模板。它只是一个函数模板,一旦实例化,就会生成乘法运算符的重载