[C++]private継承によるfriendなoperator

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
zxc
記事: 79
登録日時: 13年前
住所: 日本の背骨(?)あたり

[C++]private継承によるfriendなoperator

#1

投稿記事 by zxc » 10年前

  上のコードはコメント部分に関してリンクエラー( error LNK2001: 外部シンボル ""bool __cdecl zxc::math::operator==(struct test const &,struct test const &)" (??8math@zxc@@YA_NABUtest@@0@Z)" は未解決です。)がでますが、下のコードだとリンクエラーがでませんでした。自分には似たようなことをしている2クラスに見えますし、エラーが出る方はエラーが出ない方のコードのコピーからほとんど出来ているといえると思います。(実際そうやって作りました)

 どちらもクラスや構造体にprivate継承されることで、継承したクラスの自己型を引数にとるoperatorの委譲を目標にしています。現時点ではoperator!=の実装をoperator==に委譲する部分が失敗するのだと思いますが、operator!=の実装外でのoperator==の使用ではエラーは起こらないように思えます。 環境はWin7 MSVSC++2010です。(提示しているコードは実際のものと大差ありませんが、名前空間等一部分を消したりしているのでそのままではコンパイル等が通らない可能性があります)
 
 このとき
  1. 何がエラーの原因なのか
  2. 今回のようなエラーを未然に防ぐような工夫にはどのようなものが挙げられるのか
以上2点を教えてください。

コード:

//エラーが出る方
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 );
		}
*/
};

コード:

//動く方
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);
		friend inline type&& operator+(const type& t,const type& other){
			type temp;
			temp += t;
			temp += other;
			return std::move(temp);
		}
};
実際にprivate継承させてみる例
 

コード:

#include"addable.hpp"
#include"Comparable.hpp"


struct test:
	Equalable<test>
	,Addable<test>{
		int x;
		
		test(int i=0):x(i){}
		bool operator==(const test& other){
			return (this->x == other.x);
		}		
		test& operator+=(const test& other){
			this->x +=other.x;
			return (*this);
		}
};
 実際にエラーが出るmain.cpp

コード:

#include<iostream>

#include"Testructure.hpp"

using namespace std;

int main(){

	test a(4),b(13);
	
	cout<<boolalpha;
	cout<<( a == b );//<<","<< (a != b)<<endl;//ここ
	return 0;
}

sleep

Re: [C++]private継承によるfriendなoperator

#2

投稿記事 by sleep » 10年前

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

Re: [C++]private継承によるfriendなoperator

#3

投稿記事 by sleep » 10年前

理解できる方とできない方がいるかもしれないので・・・
もう少し書くと、以下の定義の場合、左辺値は constだと呼び出せないということですね。

コード:

        bool operator==(const test& other){
            return (this->x == other.x);
        }       
        test& operator+=(const test& other){
            this->x +=other.x;
            return (*this);
        }

アバター
milfeulle
記事: 47
登録日時: 11年前
住所: マリーランド
連絡を取る:

Re: [C++]private継承によるfriendなoperator

#4

投稿記事 by milfeulle » 10年前

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で定義した==とシグネチャが同じとなって曖昧になってしまうからです。参考サイトのオーバーライド出来ないというのはこのことですね。
ζ*'ヮ')ζプログラミングはみんなで奏でるシンフォニー

zxc
記事: 79
登録日時: 13年前
住所: 日本の背骨(?)あたり

Re: [C++]private継承によるfriendなoperator

#5

投稿記事 by zxc » 10年前

 
 皆さん返信していただきありがとうございました。
 「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;
}

sleep

Re: [C++]private継承によるfriendなoperator

#6

投稿記事 by sleep » 10年前

zxc さんが書きました: operator=が(デフォルトであるかどうかに関わらず)正常に機能することも期待することになると思うので
別件ではありますが

コード:

type temp;
temp = t;

コード:

type temp = t;
では、前者は operator= が呼ばれますが、
後者は呼ばれないので、失念の際は気を付けてくださいね。

zxc
記事: 79
登録日時: 13年前
住所: 日本の背骨(?)あたり

Re: [C++]private継承によるfriendなoperator

#7

投稿記事 by zxc » 10年前

 そういえば確かコピーコンストラクタが呼ばれるんでしたね。気をつけます。

閉鎖

“C言語何でも質問掲示板” へ戻る