構造体の暗黙のコンストラクタ?
どうしても高速化したい処理があって久々にC++を書いています。
が、書いていてあれ?と思うことがあったので記事を書いてみます。
サンプルコードは以下のようなものです。
#include<iostream> #include<string> #include<map> struct point { int x; int y; }; void disp(const point& pt) { std::cout << "(" << pt.x << ", " << pt.y << ")" << std::endl; } int main(){ //garbage value point pt1; disp(pt1); // (0, 0) std::map<std::string, point> m; disp(m["hoge"]); // (0, 0) implicit constructor? point pt2 = point(); disp(pt2); // (0, 0) initialize point pt3 = {}; disp(pt3); return 0; }
//garbage value
point pt1;
disp(pt1);
これがゴミを表示するのはCの感覚で言えば納得です。
ところが
// (0, 0) std::map<std::string, point> m; disp(m["hoge"]);
なんとこれは "(0, 0)" と表示されるのでした。
std::map の operator[] は、キーに対応するエントリが存在しない場合にはデフォルトコンストラクタでオブジェクトを作る的な理解をしていましたが、pt1のケースと挙動が異なるわけです。
えっ?と思って使っているコンパイラのSTLのmapのソースなどを読んでみたり色々試していると、どうも
// (0, 0) implicit constructor?
point pt2 = point();
disp(pt2);
このような書き方では "(0, 0)"に初期化されるようなのでした。
C++の構造体はpublicがデフォルトである以外はclassと同じというのは知っています。
と言うことはC的な記述で構造体を宣言した場合は、コンパイラが暗黙のコンストラクタやらコピーコンストラクタなどを作ったりするのだろうという思い込んでいたのですが、しかし pt1 の書き方と pt2 の書き方は等価であると思っていました。
まあただ、以下のような書き方(これもpt1やpt2と等価でデフォルトコンストラクタが呼ばれると思っていました)はコンパイルエラーになったので何か勘違いしているのかもしれません。
// compile error
point pt4();
disp(pt4);
普通であれば class にしてコンストラクタも明示的に与えるべきなのでしょうが、高速化が目的なので可能でありかつ仕様に則った挙動であるならば余分なことを書かずに済ませたいのですが、いやはや・・・
プログラミング言語C++をざっと眺めただけでは、この挙動を正当化する記述は見つけられませんでした。となるとC++の仕様書を読むべきなのでしょう。あんまり読みたくないし、読んでも処理系が仕様に則っているかどうかも定かではないという。。