6

私は C++ を学んでおり、2 つの異なる型のインスタンスで機能する二項演算子を作成する好ましい方法について、ある程度の洞察を得ることができるかどうか疑問に思っていました。以下は、私の懸念を説明するために作成した例です。

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;
};

2 つのタイプの間で対称性を持たせたい場合、上記のコードではどちらの方法が最適な方法でしょうか。ある方法を他の方法よりも優先して選択することに危険はありますか? これは戻り値の型によって異なりますか? 説明してください!ありがとうございました!

4

4 に答える 4

10

最善の方法は、(いずれかのクラスの外で) を定義しint operator+ (const A& a, const B& b)、必要に応じて両方のクラスのフレンド関数にすることです。さらに、

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

左右対称にします。

于 2009-03-31T19:28:10.470 に答える
4

このアプローチの大きなリスクは、人々が + を対称演算子として認識する傾向があることです。これが書かれている方法は、そうではありません(実装が同じでない限り)。

少なくとも、+ を (メンバーとしてではなく) 外部二項演算子としてオーバーロードしてから、それを数回オーバーロードして遊んでください。

ただし、何も曖昧にならないように注意する必要があります。

何をしようとしているのか説明できますか?対称異種演算子を持つことが理にかなっている、さまざまなタイプの多くのケースは考えられません。

于 2009-03-31T19:26:31.020 に答える
1

コメントで、あなたの使用目的はベクトルと行列の追加であると読みました。ベクトルが 1 次元行列である行列のみを使用することを検討する必要があるかもしれません。次に、型と演算子のセットが 1 つだけ残ります。

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

ベクトル クラスを保持したい場合は、転置されたベクトルも必要かどうかを検討する必要があります (転置はベクトルの内部プロパティにすぎない可能性があります)。

一連の操作は実際には対称的ではありません。

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

これら3つの操作を提供する必要があります(転置がベクトルのプロパティであると仮定します):

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

可能であればすべて無料の機能として。上記のセットが対称でなくても、現実の世界でもそうではなく、ユーザーはそれを期待します。

于 2009-04-01T06:23:39.147 に答える
1

方法 2 の主な引数は、2 番目のオペランドだけでなく、両方のオペランドで暗黙的な型変換が行われることです。これにより、どこかで混乱が回避される可能性があります。

そういえば、コード例では、両方のクラスの 1-arg コンストラクターを介して、int から A および int から B への暗黙的な変換を定義しています。これにより、後であいまいさが生じる可能性があります。ただし、簡潔にするために「明示的」を省略した場合でも、十分です。

ただし、Uri の警告には同意します。これを行うと、他の人が混乱する API を作成している可能性があります。なぜ A と B が int なのですか? getX を自分で呼び出して結果を追加するよりも、a と b を追加するほうが、ユーザーにとって本当に簡単になるのでしょうか?

A と B が int のラッパーであることをユーザーがよく知っているからでしょうか? その場合、別のオプションは、A から int への変換と B から int への変換を演算子 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";
}
于 2009-03-31T19:38:57.183 に答える