具有不同类型的C ++对称二元运算符

时间:2021-08-01 16:05:01

I am learning C++ and I was wondering if I could gain some insight into the preferred way of creating binary operators that work on instances of two different types. Here is an example that I've made to illustrate my concerns:

我正在学习C ++,我想知道我是否可以深入了解创建二元运算符的首选方法,这些运算符可以处理两种不同类型的实例。以下是我用来说明我的担忧的一个例子:

class A;
class B;

class A
{
    private:
        int x;

    public:
        A(int x);

        int getX() const;

        int operator + (const B& b);
};


class B
{
    private:
        int x;

    public:
        B(int x);

        int getX() const;

        int operator + (const A& A);
};


A::A(int x) : x(x) {}

int A::getX() const { return x; }

// Method 1
int A::operator + (const B& b) { return getX() + b.getX(); }


B::B(int x) : x(x) {}

int B::getX() const { return x; }

// Method 1
int B::operator + (const A& a) { return getX() + a.getX(); }


// Method 2
int operator + (const A& a, const B& b) { return a.getX() + b.getX(); }

int operator + (const B& b, const A& a) { return a.getX() + b.getX(); }


#include <iostream>

using namespace std;

int main()
{
    A a(2);
    B b(2);

    cout << a + b << endl;

    return 0;
};

If I would like to have symmetry among the two types, which method is the best approach in the above code. Are there any possible dangers in choosing one method over the other? Does this vary with the return type? Please explain! Thank you!

如果我想在两种类型中具有对称性,那么哪种方法是上述代码中的最佳方法。选择一种方法比另一种方法有任何可能的危险吗?这是否随退货类型而变化?请解释!谢谢!

4 个解决方案

#1


The best way is to define (outside of either class) int operator+ (const A& a, const B& b), and make it a friend function of both classes if needed. In addition, define

最好的方法是定义(在任一类之外)int operator +(const A&a,const B&b),并在需要时使它成为两个类的友元函数。另外,定义

int operator+(const B& b, const A& a) {return a + b;}

To make it symmetric.

使其对称。

#2


The big risk with this approach is that people tend to perceive + as a symmetric operator. The way this is written, it is not (unless your implementations re the same).

这种方法的最大风险是人们倾向于将+视为对称算子。这是写的方式,它不是(除非你的实现相同)。

At a minimum, you should overload + as an external binary operator (not as a member), and then play with overloading it several times.

至少,你应该重载+作为外部二进制运算符(而不是作为成员),然后多次重载它。

You have to be careful, though, to make sure that nothing becomes ambiguous.

但是,你必须要小心,以确保没有任何变得模棱两可。

Can you explain what you're trying to do? I can't think of many cases of different types where it makes sense to have the symmetric heterogenous operators.

你能解释一下你想做什么吗?我不能想到许多不同类型的情况,其中有对称的异质算子是有意义的。

#3


The main argument for method 2 is that you get implicit type conversion on both operands, not just the second one. This might save confusion somewhere down the line.

方法2的主要参数是您在两个操作数上获得隐式类型转换,而不仅仅是第二个操作数。这可能会在某个地方避免混淆。

Speaking of which, your example code defines implicit conversions from int to A and from int to B, via the 1-arg constructors on both classes. This could result in ambiguity later. But if you left out the "explicit" for brevity, fair enough.

说到这一点,您的示例代码通过两个类上的1-arg构造函数定义从int到A以及从int到B的隐式转换。这可能会导致后来的模糊。但是如果你为了简洁而省略了“明确”,那么公平。

I agree with Uri's warning, though: if you find yourself doing this, you may be writing an API that others will find confusing. How come an A plus a B is an int? Does it really make things easier for users that they are adding a and b, rather than calling getX themselves and adding the results?

我同意Uri的警告:如果你发现自己这样做,你可能正在编写一个其他人会感到困惑的API。为什么A加上B是一个int?是否真的让用户更容易添加a和b,而不是自己调用getX并添加结果?

Is it because users know perfectly well that A and B are wrappers for ints? If so, then another option is to expose conversions from A to int and B to int, via operator int(). Then a+b will return an int for a sensible reason, and you'll get all the other arithmetic operators too:

是因为用户非常清楚A和B是整数的包装器吗?如果是这样,那么另一种选择是通过operator int()公开从A到int和B到int的转换。然后a + b将返回一个int,这是一个合理的原因,你也将获得所有其他算术运算符:

#include <iostream>

struct A {
    int x;
    explicit A(int _x) : x(_x) {}
    operator int() {
        return x;
    }
};

struct B {
    int x;
    explicit B(int _x) : x(_x) {}
    operator int() {
        return x;
    }
};

int main() {
    A a(2);
    B b(2);
    std::cout << a + b << "\n";
    std::cout << a - b << "\n";
}

#4


I read in a comment that your intended use is adding vectors and matrices. Maybe you should consider using only matrices where vectors are one dimensional matrices. Then you are left with just one type and one set of operators:

我在评论中读到你的用途是添加向量和矩阵。也许您应该考虑仅使用向量是一维矩阵的矩阵。然后你只剩下一种类型和一组运算符:

matrix operator*( matrix const& a, matrix const& b );
matrix operator+( matrix const& a, matrix const& b ); // and so on

If you want to keep the vector class then you should consider whether you also want a transposed vector (maybe transpose is just an internal property of vector).

如果你想保留矢量类,那么你应该考虑是否还需要一个转置矢量(可能转置只是矢量的内部属性)。

The set of operations is not really symmetric:

这组操作并不是真正对称的:

vector * matrix = vector
matrix * vector_t = vector_t
matrix * matrix = matrix
vector_t * vector = matrix
vector * vector_t = int

and you should offer those three operations (assuming transpose is a property of vector):

你应该提供这三个操作(假设转置是向量的属性):

vector operator*( vector const& v, matrix const& m );
vector operator*( matrix const& m, vector const& v );
matrix operator*( matrix const& m1, matrix const& m2 );
matrix operator*( vector const& v1, vector const& v2 ); // possibly 1x1 matrix, you cannot overload changing only return value

All as free functions if possible. Even if the above set is not symmetric, neither is the real world and your users will expect it.

如果可能,全部作为免费功能。即使上面的集合不对称,现实世界也不是你的用户所期望的。

#1


The best way is to define (outside of either class) int operator+ (const A& a, const B& b), and make it a friend function of both classes if needed. In addition, define

最好的方法是定义(在任一类之外)int operator +(const A&a,const B&b),并在需要时使它成为两个类的友元函数。另外,定义

int operator+(const B& b, const A& a) {return a + b;}

To make it symmetric.

使其对称。

#2


The big risk with this approach is that people tend to perceive + as a symmetric operator. The way this is written, it is not (unless your implementations re the same).

这种方法的最大风险是人们倾向于将+视为对称算子。这是写的方式,它不是(除非你的实现相同)。

At a minimum, you should overload + as an external binary operator (not as a member), and then play with overloading it several times.

至少,你应该重载+作为外部二进制运算符(而不是作为成员),然后多次重载它。

You have to be careful, though, to make sure that nothing becomes ambiguous.

但是,你必须要小心,以确保没有任何变得模棱两可。

Can you explain what you're trying to do? I can't think of many cases of different types where it makes sense to have the symmetric heterogenous operators.

你能解释一下你想做什么吗?我不能想到许多不同类型的情况,其中有对称的异质算子是有意义的。

#3


The main argument for method 2 is that you get implicit type conversion on both operands, not just the second one. This might save confusion somewhere down the line.

方法2的主要参数是您在两个操作数上获得隐式类型转换,而不仅仅是第二个操作数。这可能会在某个地方避免混淆。

Speaking of which, your example code defines implicit conversions from int to A and from int to B, via the 1-arg constructors on both classes. This could result in ambiguity later. But if you left out the "explicit" for brevity, fair enough.

说到这一点,您的示例代码通过两个类上的1-arg构造函数定义从int到A以及从int到B的隐式转换。这可能会导致后来的模糊。但是如果你为了简洁而省略了“明确”,那么公平。

I agree with Uri's warning, though: if you find yourself doing this, you may be writing an API that others will find confusing. How come an A plus a B is an int? Does it really make things easier for users that they are adding a and b, rather than calling getX themselves and adding the results?

我同意Uri的警告:如果你发现自己这样做,你可能正在编写一个其他人会感到困惑的API。为什么A加上B是一个int?是否真的让用户更容易添加a和b,而不是自己调用getX并添加结果?

Is it because users know perfectly well that A and B are wrappers for ints? If so, then another option is to expose conversions from A to int and B to int, via operator int(). Then a+b will return an int for a sensible reason, and you'll get all the other arithmetic operators too:

是因为用户非常清楚A和B是整数的包装器吗?如果是这样,那么另一种选择是通过operator int()公开从A到int和B到int的转换。然后a + b将返回一个int,这是一个合理的原因,你也将获得所有其他算术运算符:

#include <iostream>

struct A {
    int x;
    explicit A(int _x) : x(_x) {}
    operator int() {
        return x;
    }
};

struct B {
    int x;
    explicit B(int _x) : x(_x) {}
    operator int() {
        return x;
    }
};

int main() {
    A a(2);
    B b(2);
    std::cout << a + b << "\n";
    std::cout << a - b << "\n";
}

#4


I read in a comment that your intended use is adding vectors and matrices. Maybe you should consider using only matrices where vectors are one dimensional matrices. Then you are left with just one type and one set of operators:

我在评论中读到你的用途是添加向量和矩阵。也许您应该考虑仅使用向量是一维矩阵的矩阵。然后你只剩下一种类型和一组运算符:

matrix operator*( matrix const& a, matrix const& b );
matrix operator+( matrix const& a, matrix const& b ); // and so on

If you want to keep the vector class then you should consider whether you also want a transposed vector (maybe transpose is just an internal property of vector).

如果你想保留矢量类,那么你应该考虑是否还需要一个转置矢量(可能转置只是矢量的内部属性)。

The set of operations is not really symmetric:

这组操作并不是真正对称的:

vector * matrix = vector
matrix * vector_t = vector_t
matrix * matrix = matrix
vector_t * vector = matrix
vector * vector_t = int

and you should offer those three operations (assuming transpose is a property of vector):

你应该提供这三个操作(假设转置是向量的属性):

vector operator*( vector const& v, matrix const& m );
vector operator*( matrix const& m, vector const& v );
matrix operator*( matrix const& m1, matrix const& m2 );
matrix operator*( vector const& v1, vector const& v2 ); // possibly 1x1 matrix, you cannot overload changing only return value

All as free functions if possible. Even if the above set is not symmetric, neither is the real world and your users will expect it.

如果可能,全部作为免费功能。即使上面的集合不对称,现实世界也不是你的用户所期望的。