皆さん返信していただきありがとうございました。
「privateで継承されるクラスが委譲の時に内部で要求する関数とそれによって実装される関数の2つの関数を持っていなければならない」という先入観があった事が今回の心理的な原因の一つかもしれません。と御三方の返信を見て思いました。
sleep さんが書きました:const変数同士で式を形成しているとそうなりますね。
Addableの方でも以下のようにするとエラー出ますよ。
コード:
friend type operator+(const type& t, const type& other){
t += other;
type temp = t;
return temp;
}
以下、解決策の一例
コード:
friend bool operator!=(const type& t, const type& other){
type temp = t;
return !(temp == other);
}
sleep さんが書きました:理解できる方とできない方がいるかもしれないので・・・
もう少し書くと、以下の定義の場合、左辺値は constだと呼び出せないということですね。
コード:
bool operator==(const test& other){
return (this->x == other.x);
}
test& operator+=(const test& other){
this->x +=other.x;
return (*this);
}
解決策やエラーの例ではoperator=が(デフォルトであるかどうかに関わらず)正常に機能することも期待することになると思うので別の案で実装しようと思います。
変数や関数がconstなものかが今回重要なことがわかりました。
milfeulle さんが書きました:1. コメントアウトを外しても、こちらの環境〔VS2013Express〕だとコンパイルが通ってしまいますね……。
そのエラーは、==演算子が定義されていないことによると思います。a == bのとき、bのほうがconst test&として、testのほうのoperator==(const test& other)が呼ばれるのですが、
!=演算子についてはconst test&が左辺の==が見つからないので上記のようなコンパイルエラーが出ます。
オフトピック
こちらの環境では、言語仕様は知らないのですが、
コード:
friend inline bool operator!=(const type& t,const type& other){
return !( t == other );
}
が無視されてコンパイルされていたみたいですね。
どうして==が見つからないかというと、次のように簡略化したソースを考えてみます。
コード:
template<typename T>
class Equalable {
public:
typedef typename T type;
static_assert(std::is_class<type>::value, "T type of Equalable<T> needs class type but not pod.");
// ★
//friend inline bool operator==(const type& t, const type& other);
friend inline bool operator!=(const type& t,const type& other){
return !( t == other );
}
};
struct test : Equalable<test> {
int x;
test(int i = 0) :x(i) { }
bool operator==(const test& other) {
return (this->x == other.x);
}
};
int main() {
test a(4), b(13);
a == b; // ok; bool operator==(const test& other)が呼ばれる
a != b; // error; t == otherが定義されていない
return 0;
}
このとき、!=が計算できません。t == otherの際、tがconstのためにbool operator==(const test& other)が呼ばれないのです。というわけで、constメンバ関数にすることで解決できます。
コード:
bool operator==(const test& other) const {
return (this->x == other.x);
}
2. constなオブジェクトのメンバ関数を呼び出すときは、そのオブジェクトのconstメンバ関数しか呼び出せないことに注意しましょう。
ところで、恐らく
HaskellをC++で書いてみる(型クラス)のように、委譲を目的としたいのですよね。でしたらこちらのサイトを参考に書かれるといいと思います。
ちなみに、★の部分のコメントを外すとエラーとなります。というのも、struct testで定義した==とシグネチャが同じとなって曖昧になってしまうからです。参考サイトのオーバーライド出来ないというのはこのことですね。
constなメンバ関数を書くと曖昧さのエラーが出るので、private継承されるクラス側のfriendな同名の関数を取り除いて解決してやればよいのですね。
以下のコードで目標が達成できそうなことが分かったので解決にします。ありがとうございました。
コード:
////addable.hpp
template<typename T>
class Addable{
public:
typedef typename T type;
static_assert(std::is_class<type>::value,"T type of Addable<T> needs class type but not pod.");
friend inline type&& operator+(const type& t,const type& other){
type temp;
temp += t;
temp += other;
return std::move(temp);
}
};
コード:
template<typename T>
class Equalable{
public:
typedef typename T type;
static_assert(std::is_class<type>::value,"T type of Equalable<T> needs class type but not pod.");
friend inline bool operator!=(const type& t,const type& other){
return !( t == other );
}
};
コード:
//test.hpp
struct test:
Equalable<test>
,Addable<test> {
int x;
test(int i=0):x(i){}
bool operator==(const test& other)const{
return (this->x == other.x);
}
test& operator+=(const test& other){
this->x +=other.x;
return (*this);
}
};
コード:
///main.cpp
#include"Testructure.hpp"
using namespace std;
int main(){
test a(4),b(13);
auto c = a + b ;
cout << a.x<<",";
cout << b.x<<",";
cout << c.x<<",";
cout <<"\n";
cout << (a+=b).x <<","<< a.x <<","<< b.x ;
cout <<"\n";
cout << (a+=b).x <<","<< a.x <<","<< b.x ;
cout<<boolalpha;
cout<<( a == b )<<","<< (a != b)<<endl;
return 0;
}