我已经实现了一个类
vec3
:
struct vec3 {
double x = 0, y = 0, z = 0;
auto& operator*= (double d) {x *= d; y *= d; z *= d; return *this;}
};
auto operator* (const vec3 &a, double d) {auto ret = a; ret *= d; return ret;}
但是,
vec3{} * 5.
形式的表达式可以编译,而像 5. * vec3{}
这样的表达式则不能编译。这让我很困惑,因为 cppreference 这么说
二元运算符通常被实现为非成员以保持对称性(例如,当将复数和整数相加时,如果operator+是复数类型的成员函数,则只有复数+整数可以编译,而不是整数+ 复杂)。
在这里,我have以非成员身份实现了
operator*
,所以由于cppreference引用中提到的对称性,vec3{} * 5.
和5. * vec3{}
不应该编译吗?
cppreference 所说的“对称”意味着隐式转换的对称。当您将
*
实现为成员函数时,左侧不受用户定义的转换的影响。例如,
vec2::operator*
可转换为
vec3
,左侧的
vec3
也无法使用
vec2
成员函数;然而,vec2::operator*(vec2)
转换,
vec3
仍然可以接受右侧的
vec3 -> vec2
这意味着
vec2 * vec3
和 vec3 * vec2
的工作方式不同,这种不对称性令人惊讶且不受欢迎。
您所指的“对称性”是交换性,即交换操作数并为某些操作产生相同结果的能力。默认情况下,C++ 并不使运算符具有可交换性。无论如何,这样做对于大多数人来说都是不安全的,尤其是对于
*
来说。矩阵乘法不可交换,但例如使用 *
符号。
如果你想要交换律,你必须自己做:
auto operator*(const vec3 &a, double d) { auto ret = a; ret *= d; return ret; }
auto operator*(double d, const vec3& a) { return a * d; }
请注意,自 C++20 起,
!=
和 ==
即使在重载时也是可交换的。这是您必须自己定义交换性的规则的一个例外。