質問のような事例では majiponi 氏の解で十分なので余談になりますが、メンバ関数として定義した二項演算子は左辺では暗黙の型変換が働かず右辺では暗黙の型変換が働きます。
cpp
1#include <iostream>
2
3class test {
4 int d;
5 int MOD;
6
7public:
8 test(int i, int M);
9 test operator+(const test&);
10 friend std::ostream& operator<<(std::ostream&, const test&);
11};
12
13test::test(int i, int M) : d(i), MOD(M) {}
14
15test test::operator+(const test& t) {
16 return test((this->d + t.d) % this->MOD, this->MOD);
17}
18
19std::ostream& operator<<(std::ostream &os, const test &a) {
20 return os << a.d << " " << a.MOD;
21}
22
23// test に変換可能な test2 型
24class test2 {
25public:
26 operator test(void) {
27 return test(10, 10);
28 }
29};
30
31int main(void) {
32 // + の右辺が test2 型なのは OK
33 std::cout << test(3, 2) + test2() << std::endl;
34
35 // + の左辺が test2 型なのはエラーになる。
36 std::cout << test2() + test(3, 2) << std::endl;
37 return 0;
38}
このような非対称は好ましくないので既存の (引数として与えられた) オブジェクトを変更せずにあたらしいオブジェクトを生成する二項演算子に関しては非メンバ関数としてオーバーロードするのが好ましいと考えられています。 (標準ライブラリもそうなってます。)
cpp
1#include <iostream>
2
3class test{
4 int d;
5 int MOD;
6
7public:
8 test(int i, int M);
9 friend test operator+(const test&, const test&);
10 friend std::ostream& operator<<(std::ostream&, const test&);
11};
12
13test::test(int i, int M) : d(i), MOD(M) {}
14
15test operator+(const test& t1, const test& t2) {
16 return test((t1.d + t2.d) % t1.MOD, t1.MOD);
17}
18
19std::ostream& operator<<(std::ostream &os, const test &a) {
20 return os << a.d << " " << a.MOD;
21}
22
23// test に変換可能な test2 型
24class test2 {
25public:
26 operator test(void) {
27 return test(10, 10);
28 }
29};
30
31int main(void) {
32 // + の右辺が test2 型なのは OK
33 std::cout << test(3, 2) + test2() << std::endl;
34 // + の左辺が test2 型なのも OK
35 std::cout << test2() + test(3, 2) << std::endl;
36 return 0;
37}