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

发布于 07-15 12:54 字数 1102 浏览 12 评论 0原文

我正在学习 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;
};

如果我想在两种类型之间具有对称性,哪种方法是上面代码中的最佳方法。 选择一种方法而不是另一种方法是否存在任何可能的危险? 这会随着返回类型的变化而变化吗? 请解释! 谢谢你!

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:

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

落叶缤纷2024-07-22 12:54:24

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

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

使其对称。

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 B& b, const A& a) {return a + b;}

To make it symmetric.

锦欢2024-07-22 12:54:24

这种方法的最大风险是人们倾向于将 + 视为对称运算符。 就其编写方式而言,事实并非如此(除非您的实现相同)。

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

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

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

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.

陪你搞怪i2024-07-22 12:54:24

方法 2 的主要论点是,您可以对两个操作数进行隐式类型转换,而不仅仅是第二个操作数。 这可能会避免某些地方的混乱。

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

不过,我同意 Uri 的警告:如果您发现自己这样做,那么您编写的 API 可能会让其他人感到困惑。 为什么A加B是int? 对于用户来说,添加 a 和 b,而不是自己调用 getX 并添加结果,真的会让事情变得更容易吗?

是因为用户非常清楚 A 和 B 是整数的包装吗? 如果是这样,那么另一个选择是通过运算符 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";
}

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.

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.

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?

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:

#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";
}
神魇的王2024-07-22 12:54:24

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

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

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

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

向量 * 矩阵 = 向量 
  矩阵 * 向量_t = 向量_t 
  矩阵 * 矩阵 = 矩阵 
  矢量_t * 向量 = 矩阵 
  向量*向量_t = int 
  

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

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

如果可能,全部作为自由函数。 即使上述集合不对称,现实世界也不是对称的,并且您的用户也会期望它。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文