代入演算子のオーバーロードとコンパイルエラー「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 名前は名無しです 名前は太郎です $
ここまでくると自己満足も甚だしいか・・・。