如何为模板化类声明类外二进制运算符

How does one declare an out of class binary operator for a templated class?

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

我有一种情况,我有一些数学运算,为了方便起见,可以重载。

但其中一些在其他类型上运行。

vec3<type> * type

type * vec3<type>

标量的右手参数的一种方法是:

template<class T, class SubT>
class Vec3 {
// this is fine, ie: works
SubT operator *(const T &val) {
return(SubT(x*val,y*val,z*val));
}
};

我读过,最好只在"类外"实现*、+、-、/等的运算符,或者让编译器从类中的+=版本中推导出一些内容。

  • 与在类中实现+相比,这是最优的吗
  • 如果左手边的参数是另一种类型,那么如何对相反的情况进行处理

Ie在我的特殊情况下,模板化运算符有两个模板类型参数。一个是元素的类型,另一个是模板实现其方法的超类。

template<class T, class SubT>
SubT operator *(const T &a, const Vec3Base<T, B> &b) { 
return(b * a); 
}

无论如何,希望你能得到我的愿望,如何正确地做到这一点是个问题:)

就像我只需要做一种类型吗?即:向量类型,然后从中获得元素类型作为typedef??

template<class VT>
VT::SubT operator*(const VT::ElemT &a, const VT &v) {
return(v * a);
}

我是否也应该用来实现另一种方式,而不是在类中而是"类外">

template<class VT>
VT::SubT operator*(const VT &a, const VT::ElemT &b ) {
return(VT::SubT(a.x*b,a.y*b,a.z*b));
}

我确实读了运营商过载问题的大部分答案。

我确实回答了很多问题。但是不包括模板的ramafacations和在这些模板的子类中使用的声明运算符的模板。

对于所有必须选择将其作为成员函数或非成员函数实现的运算符,请使用以下经验法则来决定:

这在一定程度上有助于我了解实现的最佳方式,无论是在课堂外还是在课堂内。

If it is a unary operator, implement it as a member function.
If a binary operator treats both operands equally (it leaves them unchanged), implement this operator as a non-member function.
If a binary operator does not treat both of its operands equally (usually it will change its left operand), it might be useful to make

如果必须访问,它是其左操作数类型的成员函数操作数的私有部分。

我想知道模板的优先级是否存在问题。我发现,如果一个运算符是在模板中声明的,并且它是继承其运算符的子类的超类,至少在MS编译器中,它会优先考虑全局运算符,而不是超类中的运算符。肮脏的clang和gcc也会出现类似的问题。

我确实发现我真的必须在同一级别解密所有可能冲突的运算符,以便过载解决方案如预期那样工作。ie:所有这些都在子类的同一个超类中,如果在该超类的超类中声明了poerator,它们有时会被忽略——如果有一些任性的转换为优先级较高的重载之一(arrrgh)提供了一个参数。

在这一点上,我似乎已经解决了所有的编译问题——现在把它链接起来哈哈哈!!

假设你的类型移动起来很便宜:

template<class T, class SubT>
class Vec3 {
// this is fine, ie: works
SubT& operator *=(const T &val) & {
x*=val;y*=val;z*=val;
return *this;
}
friend SubT operator*(Vec3 v, T const& t){
v*=t;
return v;
}
friend SubT operator*(T const& t, Vec3 v){
v*=t;
return v;
}
};

一个3元组的数字移动起来几乎总是很便宜的,因为这些数字要么很小(比如64位),而且很难复制,要么它们将是一个bignum类型,在内部使用廉价的移动存储类型。

这种技术创造了我所说的柯尼希算子;非模板运算符onky可通过ADL从模板类中发现。与成员函数运算符和非成员模板运算符相比,这有许多优点。在这种简单的情况下,情况就不那么简单了,但作为一个盲目的接受者,它避免了一堆陷阱(比如,使用运算符std::string不会让你成为<<流)。