ページ 11

[C++]vector<unique_ptr<T>>の初期化

Posted: 2017年3月19日(日) 23:18
by purin52002
こんにちは
スマポを知ってから生ポwwwぬるぽwwwな者です。

今回はboostではなくc++標準のunique_ptrについて質問があります。

コード:

class Base;//定義は省略
class A : public Base;//同上
class B : public Base; //同上

int main()
{
    vector<unique_ptr<Base> > p_vec  { make_unique<A>(), make_unique<B>() } ;//error

    p_vec.push_back(make_unique<A>() );//こっちはセーフ
}
上のようなコードで、vectorの初期化時にunique_ptrをつっこむとエラーが出ます。
おそらく内部でコピーコンストラクタが呼ばれているのかな?と思います。

現在はpush_backを使って一つづつ格納していますが、醜いような気がします。
(あと、新しい要素を追加するときにpush_backって書くのがめんどくさい^^;

ここで質問なのですが、vectorの初期化時にunique_ptrを格納することは可能でしょうか?
また、もし可能であるならそのやり方を教えてもらいたいです。

よろしくお願いします<(_ _)>

Re: [C++]vector<unique_ptr<T>>の初期化

Posted: 2017年3月21日(火) 13:00
by inemaru
purin52002 さんが書きました: ここで質問なのですが、vectorの初期化時にunique_ptrを格納することは可能でしょうか?
また、もし可能であるならそのやり方を教えてもらいたいです。
自前で、格納する機能を作る必要があると思います。

例えば、
初期化リストを受け取って、vector<unique_ptr<Base>> を返却する関数を作れば可能。
手元で確認していませんが、関数のイメージとしては、
初期化リスト受け取り→reserveで要素数指定→emplace_backで格納
が良いと思います。
オフトピック
purin52002 さんが書きました: 現在はpush_backを使って一つづつ格納していますが、醜いような気がします。
(あと、新しい要素を追加するときにpush_backって書くのがめんどくさい^^;
特に理由がないのであれば、emplace_backの方が効率良いと思います。

Re: [C++]vector<unique_ptr<T>>の初期化

Posted: 2017年3月22日(水) 00:10
by purin52002
inemaruさん
素晴らしいアイデアをありがとうございます。
目から鱗でした。
早速使わせていただきたいと思います。

emplace_backについてなのですが、
私の認識ではコピーしないpush_backのイメージでした。
unique_ptrはコピーができない(moveされる)のでpush_backでもいいのかな、と思っていたのですが
やはりemplace_backのほうが効率がいいのでしょうか?
(名前が複雑という理由だけで使わず嫌いを起こしています^^;)

Re: [C++]vector<unique_ptr<T>>の初期化

Posted: 2017年3月22日(水) 00:29
by zeek
purin52002 さんが書きました: 現在はpush_backを使って一つづつ格納していますが、醜いような気がします。
(あと、新しい要素を追加するときにpush_backって書くのがめんどくさい^^;

ここで質問なのですが、vectorの初期化時にunique_ptrを格納することは可能でしょうか?
初期化ではありませんが、一旦 配列 か array を使用して move した方がスマートかと...

コード:

	array<unique_ptr<Base>, 2> a = { make_unique<A>(), make_unique<B>() };
	vector<unique_ptr<Base> > p_vec;
	std::move(a.begin(), a.end(), back_inserter(p_vec));

Re: [C++]vector<unique_ptr<T>>の初期化

Posted: 2017年3月22日(水) 03:31
by inemaru
purin52002 さんが書きました: emplace_backについてなのですが、
私の認識ではコピーしないpush_backのイメージでした。
unique_ptrはコピーができない(moveされる)のでpush_backでもいいのかな、と思っていたのですが
やはりemplace_backのほうが効率がいいのでしょうか?
(名前が複雑という理由だけで使わず嫌いを起こしています^^;)
emplace_backは直にメモリに格納するので、
一時変数のmoveが走らない分、効率が良いと考えています。

関数の実装については、
zeek さん提示の方法で実装した方がスマートに書けますね。

コード:

#include <memory>
#include <vector>

struct Base{};
struct A : public Base{};
struct B : public Base{};

// vector<unique_ptr<Base>>を返却する
template <class BaseType, class... Args>
std::vector<std::unique_ptr<BaseType>> make_unique_ptrs(Args&&... args)
{
	using namespace std;
	unique_ptr<BaseType> init[] { forward<Args>(args)... };
	return vector<unique_ptr<BaseType>> { make_move_iterator(begin(init)), make_move_iterator(end(init)) };
}

int main()
{
	using namespace std;
	auto ptrs = make_unique_ptrs<Base>(make_unique<A>(), make_unique<B>());
	return 0;
}

Re: [C++]vector<unique_ptr<T>>の初期化

Posted: 2017年3月22日(水) 12:10
by purin52002
zeekさん
ありがとうございます。おかげでうまくいきました。
生の配列なんて久しく使っていなかったのですが、配列のことちょっとなめてましたね、、、

inemaruさん
初期化リストを使えと聞いて真っ先に思い浮かんだのがinitializer_listでした。
それからinitializer_listで実装してみたところ「A型とB型の初期化子ってなんやねん」と怒られ、「Base型のリストだよ」と明示していました。
とても醜かったのでまたこちらで質問するかどうかで悩んでいたところにinemaruさんの追記でした^^

可変引数に右辺値参照にforwardに、、、
実際に使うのはどれも初めてでした^^;テンプレート引数を可変にできることなんて初めて知りました^^;
おかげですっきりしたコードにできましたし、自分の技量のなさを改めて実感しましたorz

なにはともあれ、おかげさまで無事解決です^^
ありがとうございました。
オフトピック
inemaru さんが書きました: emplace_backは直にメモリに格納するので、
一時変数のmoveが走らない分、効率が良いと考えています。
一時変数が作られないということで、今後は徐々にemplace_backに置き換えていこうと思います^^