Awesome Hacks!

プログラミング初心者なので地道に勉強していきます。分からない人の立場から整理していきます。

代入演算子のオーバーロードとコンパイルエラー「no viable overloaded」

※あらかじめお伝えしておきますが、当ブログの著者は初心者です。本稿についても、個人的に検討したもので、必ずしも正しいとは限りませんので、ご容赦ください。


期待する実行結果は、
 
【実行結果】

$ test
名前は名無しです
名前は太郎です
$


以下のプログラムでエラーになった。


 

#test.cpp
#include <iostream>
#include <string>

using namespace std;

class Human {
	private:  // この行は無くても可
	public:
		Human();
		virtual void out_name();  // virtualをつけると仮想関数になる
};

class Student : public Human {
	private:  // この行は無くても可
	public:
		Student();
		void out_name();
};

Human::Human(){
}

void Human::out_name(){
	cout << "名前は名無しです" << endl;
}

Student::Student(){
}

void Student::out_name(){
	cout << "名前は太郎です" << endl;
}

int main() {
	Human p1, p2;
	p1 = new Human();
	p2 = new Student();    // 派生クラスは基底クラスのポインタに格納できる
	p1->out_name();
	p2->out_name();
}

$ g++ test1.cpp -o test
prac5_1.cpp:36:5: error: no viable overloaded '='
        p1 = new Human();
        ~~ ^ ~~~~~~~~~~~
prac5_1.cpp:6:7: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'Human *' to
      'const Human' for 1st argument; dereference the argument with *
class Human {
      ^
prac5_1.cpp:37:5: error: no viable overloaded '='
        p2 = new Student();    // 派生クラスは基底クラスのポインタに格納できる
        ~~ ^ ~~~~~~~~~~~~~
prac5_1.cpp:6:7: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'Student *' to
      'const Human' for 1st argument; dereference the argument with *
class Human {
      ^
prac5_1.cpp:38:4: error: member reference type 'Human' is not a pointer; maybe you meant to use '.'?
        p1->out_name();
        ~~^~
          .
prac5_1.cpp:39:4: error: member reference type 'Human' is not a pointer; maybe you meant to use '.'?
        p2->out_name();
        ~~^~
          .
4 errors generated.
$


 
これは単純に、

//	Human p1, p2;
	Human *p1, *p2;

とするだけ。
ようは、

prac5_1.cpp:36:5: error: no viable overloaded '='
       p1 = new Human();
        ~~ ^ ~~~~~~~~~~~

の左辺と右辺が異なる「型」だったということみたい。
 
 
 

別解? 代入演算子オーバーロード

これじゃ納得いかない部分もあるので、もう少し検証。
(※こちらのサイトを参考にしました(結構分かりやすい)→C++マニアック,オペレータのオーバーロード,operator overload,演算子のオーバーロード,演算子,C++入門,C++言語講座
 

prac5_1.cpp:36:5: error: no viable overloaded '='
       p1 = new Human();
        ~~ ^ ~~~~~~~~~~~

 
について、
no viable overloaded '='
 
「viable」 : 実行できない、生存できる(形容詞)
 
つまり"「=」は実行できないオーバーロード"ということ(?)。
 
いや、個人的な見解としては"実行できる「=」演算子オーバーロードがありませんよ"ということなのではないか、というのが(初心者ながらの)結論。
 
元々、本来は自分がポインタにしたかったが間違えて「*p1」としていなかったため、上記の修正が正しい。
しかし、コンパイラのエラーの対処としては間違っている(と思う)。
なぜなら、コンパイラは、「=」演算子、すなわち代入演算子オーバーロードの問題を指摘しているにも関わらず、そこに対する解決ではないからである。
 
ここで代入演算子の観点から問題と向き合ってみる。
 
右辺はHuman型の一時オブジェクトのアドレスである。
Human型のp1の初期化のために、Human型の一時オブジェクトのアドレスを格納しようとして「=」の代入演算子を使おうとするが、Humanクラスには”Human型オブジェクトのアドレスを受けとってHuman型(アドレスではない)を返す”「=」演算子オーバーロードが定義されていない。(Humanクラスを継承したStudentクラスも同様である。)
では自分で定義してあげればよいのではないか?
ということで、無理やり(理屈が分かっていて、意図的であれば「無理やり」でもないのかもしれないが)、

//	Human p1, p2;
	Human *p1, *p2;

このような修正をしないやり方をやってみる。
 

#test2.cpp
#include <iostream>
#include <string>

using namespace std;

class Human {
	private:  // この行は無くても可
	public:
		Human();
		virtual void out_name();  // virtualをつけると仮想関数になる
		Human& operator=(Human* p) {
			return *p;
		}
};

class Student : public Human {
	private:  // この行は無くても可
	public:
		Student();
		void out_name();
		Student& operator=(Student* p) {
			return *p;
		}
};

Human::Human(){
}

void Human::out_name(){
	cout << "名前は名無しです" << endl;
}

Student::Student(){
}

void Student::out_name(){
	cout << "名前は太郎です" << endl;
}

int main() {
	Human p1;
	p1 = new Human();
	Student p2;
	p2 = new Student();    // 派生クラスは基底クラスのポインタに格納できる
	p1.out_name();
	p2.out_name();
}


【実行結果】

$ test2
名前は名無しです
名前は太郎です
$


応用編 shared_ptrを使う

完全に蛇足だが、何となく覚えたてのshared_ptrを使いたくなったので、shared_ptrを使って書き換えてみようと思う。
 

#test3.cpp
#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>

using namespace std;

class Human {
	private:  // この行は無くても可
	public:
		Human();
		virtual void out_name();  // virtualをつけると仮想関数になる
};

typedef boost::shared_ptr<Human> HumanPtr;

class Student : public Human {
	private:  // この行は無くても可
	public:
		Student();
		void out_name();
};

Human::Human(){
}

void Human::out_name(){
	cout << "名前は名無しです" << endl;
}

Student::Student(){
}

void Student::out_name(){
	cout << "名前は太郎です" << endl;
}

int main() {
	HumanPtr p1(new Human());
	HumanPtr p2(new Student());
//	HumanPtr p1, p2;       // shared_ptrは宣言と初期化は同時にやる必要がある
//	p1 = new Human();
//	p2 = new Student();
	p1->out_name();
	p2->out_name();
}


【実行結果】

$ test3
名前は名無しです
名前は太郎です
$


ここまでくると自己満足も甚だしいか・・・。