■
unique_ptrを知ってから大変重宝している。
ほぼコスト0なのにdeleteをいちいち書かずに済むのでまさにスマートなポインタ。
が、しかし、見えないが故にエラーが出ると厄介だ。
次のようなプログラムを書いた。
Dataクラスにunique_ptrで確保したメモリがありそこにデータを入れる。
それをvectorにいれてデータ配列を作ろうとした。よくありそうな場面だ。
#include <memory> #include <vector> class Data { public: std::unique_ptr<char> Buffer; size_t Size; Data(char* _buf, size_t size) : Buffer(new char [size]) , Size(size) { memcpy(Buffer.get(), _buf, size); }; }; int main() { std::vector<Data> list; const int MEM_SIZE = 128; std::unique_ptr<char> mem(new char [MEM_SIZE]); Data d(mem.get(), MEM_SIZE); list.push_back(d); return 0; }
しかしこれをコンパイルしたところ次のエラーが表示された。(Visual Studio 2012)
error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : private メンバー (クラス 'std::unique_ptr<_Ty>' で宣言されている) にアクセスできません。
ネットで調べてみると、次のページがヒントになった。
http://stackoverflow.com/questions/22788275/stdvectorstdunique-ptrint-does-not-compile
どうやらコピーコンストラクタに原因がありそうだということだが、一体どこから呼びだそうとしているのか、地道に一行ずつコメントアウトして調べていくとpush_back()が原因だとわかった。(すぐに気付きたかった)
vectorにpuch_backで要素を入れる際、要素をコピーして渡す。その時、コピーコンストラクタが働く。そして、今回のDataクラスにはunique_ptrで確保されたメンバ変数がある。unique_ptrは代入不可・コピー不可なので、デフォルトコピーコンストラクタではコピーすることができず、上記のエラーが発生したということだった。
原因が分かったところで、Dataクラスにコピーコンストラクタを実装したところコンパイルに成功した。
Data(const Data& rhs) : Buffer(new char [rhs.Size]) , Size(rhs.Size) { // Buffer = std::move(d.Buffer); memcpy(Buffer.get(), rhs.Buffer.get(), Size); }
unique_ptrを移動させる定石であるmove関数はここでは使えない。引数がconstなので変更を加えることができないためだ。
ということで、昔ながらのmemcpyを使用した。
スマートポインタは便利なだけに、ハマったときの解決策が分からなすぎて怖い。