[c++]std::unique_ptrをコピーしたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
purin52002
記事: 235
登録日時: 3年前
連絡を取る:

[c++]std::unique_ptrをコピーしたい

#1

投稿記事 by purin52002 » 3年前

こんにちは、今回はunique_ptrについて質問があります。

コード:

struct Base;//純粋仮想クラス
struct A : public Base;
struct B : public Base;

struct MyClass
{
    std::unique_ptr<Base> p;
    MyClass(const MyClass &obj)//??
    {
        this->p = std::move(obj.p);//constついてるから無理
        this->p=make_unique//objのpがAかBかわからない
        *this->p=*obj.p;//new してないから値が入らない?
    }
}
上のコードのように基底クラスのunique_ptrをメンバに持つクラスのコピーコンストラクタはどのように書けばいいのでしょう?

引数にはconst修飾子が付いていますがobjの中身はどうなっても構いません。

よろしくお願いします<(_ _)>
c++初心者を自負しています。
質問者さんには今後私にプログラミングを教えてくれるようにやさしく丁寧に教えるつもりです。ぎぶあんどていく^p^
回答者さんには精一杯感謝します。ぎぶおんりー^p^

zeek

Re: [c++]std::unique_ptrをコピーしたい

#2

投稿記事 by zeek » 3年前

purin52002 さんが書きました: unique_ptrをメンバに持つクラスのコピーコンストラクタはどのように書けばいいのでしょう?
unique_ptr をメンバに持つのであれば、コピーコンストラクタと代入演算子は使用禁止にするのが自然なのでは?
「obj の中身はどうなっても構いません」からはムーブコンストラクタがあれば充分なのでは?
こんな感じにしたいのかな?

コード:

struct MyClass
{
    std::unique_ptr<Base> p_;
    explicit MyClass(std::unique_ptr<Base>&& p) : p_(std::move(p)) {}
    explicit MyClass(MyClass &&obj) { this->p_ = std::move(obj.p_); }
private:
    MyClass(const MyClass &);              // copy constructor 禁止
    MyClass &operator=(const MyClass &);   // 代入演算子禁止
};

アバター
purin52002
記事: 235
登録日時: 3年前
連絡を取る:

Re: [c++]std::unique_ptrをコピーしたい

#3

投稿記事 by purin52002 » 3年前

zeekさん
最終的にはコンテナにクラスを格納して、stlの関数を使いたいと思っています。
その際にコピーコンストラクタやoperator=が呼ばれるようです。(だからpublicで定義したい

実は躓いていたところは自己解決できてしまったのですが、引き続き回答をお待ちしております。

躓いていたこと:Pimplイディオムを持つクラスのstd::reverse

コード:

struct MyClass
{
    struct Impl;
    std::unique_ptr<Impl> pimpl;
    MyClass();
};

struct Super;
struct SubA : public Super;
struct SubB : public Super;

struct MyClass::Impl
{
    std::unique_ptr<Super> p_obj;
    Impl();
};

int main()
{
    std::vector<MyClass> obj_list {MyClass(), MyClass()};
    std::reverse(std::begin(obj_list), std::end(obj_list) );//error 内部でoperator=が呼び出されてるっぽい
}
これの解決策として、MyClassにoperator=を作ってあげる。

コード:

struct MyClass
{
    struct Impl;
    std::unique_ptr<Impl> pimpl;
    MyClass();
    MyClass& operator=(const Myclass &obj)//constの参照型じゃないとerror
    {
        this->pimpl=std::make_unique<Impl>(*obj.pimpl);//obj.pimplの内容をコピーして新たなポインタを作る
        return *this;
    }
};

struct MyClass::Impl
{
    std::unique_ptr<Super> p_obj;
    Impl();
    Impl(Impl &obj) : p_obj(std::move(obj.p_obj) ) {}//こっちはconstついてなくてもセーフ
};
このようにして今回私が躓いていたことは解決できたのですが、
元の質問のように、継承クラスのunique_ptrを持つクラスのコピーの仕方が依然わからないのですorz

引き続きよろしくお願いします。<(_ _)>
オフトピック
お返事本当にありがとうございました。
このまま埋もれてしまうのでは、、、と若干不安だったのです^^;
c++初心者を自負しています。
質問者さんには今後私にプログラミングを教えてくれるようにやさしく丁寧に教えるつもりです。ぎぶあんどていく^p^
回答者さんには精一杯感謝します。ぎぶおんりー^p^

zeek

Re: [c++]std::unique_ptrをコピーしたい

#4

投稿記事 by zeek » 3年前

purin52002 さんが書きました: 継承クラスのunique_ptrを持つクラスのコピーの仕方が依然わからないのですorz
すべての派生クラスがわかっている前提でいいかわかりませんが...
typeid 使用して unique_ptr 先のオブジェクト自体をコピーするぐらいかと思います。

コード:

#include <typeinfo>
...
    MyClass(const MyClass &obj) {
        if (typeid(*obj.p_) == typeid(A)) {
            this->p_ = make_unique<A>();
            *this->p_ = *obj.p_;
        } else if (typeid(*obj.p_) == typeid(B)) {
            this->p_ = make_unique<B>();
            *this->p_ = *obj.p_;
        } else {
            cout << "Error copy construct" << endl;
        }
    }

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: [c++]std::unique_ptrをコピーしたい

#5

投稿記事 by YuO » 3年前

std::reverseに限定して書くならば,*firstがswappableであることを要求します。
つまり,今回に関しては,std::swap<MyClass>を特殊化し,その中でpimplをswapすればよいことになります。
まぁ,特殊化とはいえ外部からprivateなメンバを弄るのは気持ちが悪いので,
swapメンバ関数を用意して特殊化されたstd::swapから呼ぶ事になると思いますが。
ref) ISO/IEC 14882:2011
・17.6.3.2 Swappable requirements
・25.3.10 Reverse ¶2
ただ,24.4.1 Iterator traits ¶5には,非標準ながらも
[ Example: To implement a generic reverse function, a C++ program can do the following:
と書いた後にstd::swapを使わないコードが書かれているので,世の中にはstd::swapを使わないstd::reverseが提供されている可能性はあります。
オフトピック
一応,ISO/IEC 14882:1998の段階で,reverse関数はapplies swapという表記でswap関数を使う事は要求されているのため,std::swapを使わない可能性は低いですが。
なお,関数によって要素に対する要求が違います。
<algorithm>まわりだと、std::shared_ptrの方が適当なことも多いかと思います。
どういう要件になっているかは,各関数の要件を見るしかないのですが……。

アバター
purin52002
記事: 235
登録日時: 3年前
連絡を取る:

Re: [c++]std::unique_ptrをコピーしたい

#6

投稿記事 by purin52002 » 3年前

zeekさん
その発想はありませんでした。
decltypeを使って何とかできないかな、と試行錯誤したことはあったんですが(結果失敗^^;)
全派生クラスを直書きするという発想はありませんでした。
ただ、やはり汎用性というか、後から派生クラスを追加することも考えて出来れば直書きしない方法があればなー、と高望みしたりしなかったり、、、^^;

YuOさん
swapの特殊化とは、、、俺に考え付かないことを平然と思いつくッ!
しかし、そうですか、、、swapを使わないこともあるのですか、んー、、、
shared_ptrを使うことも検討したのですが、

コピー(swapの副産物として)がしたい
でも、複数のクラスとリソースを共有したいわけじゃない

という理由で、shared_ptrを使うのは適当じゃないかなーと思いまして^^;
c++初心者を自負しています。
質問者さんには今後私にプログラミングを教えてくれるようにやさしく丁寧に教えるつもりです。ぎぶあんどていく^p^
回答者さんには精一杯感謝します。ぎぶおんりー^p^

sleep

Re: [c++]std::unique_ptrをコピーしたい

#7

投稿記事 by sleep » 3年前

purin52002 さんが書きました: 元の質問のように、継承クラスのunique_ptrを持つクラスのコピーの仕方が依然わからないのですorz
私は読解力が無いためイマイチ理解できてませんが、以下ができれば良いのですか?
・std::unique_ptr でポリモーフィズムを確保する
・std::reverse が扱える

SFINE など重要でないところは省きます。

コード:

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <algorithm>

using namespace std;

class Base
{
protected:
	int i;
public:
	Base(int i) : i(i) {}
	Base(const Base& obj) { i = obj.i; }
	virtual ~Base() { cout << "~Base() : i = " << i << endl; }

	virtual string get_status()
	{
		stringstream ss;
		ss << "i = " << i << " : " << typeid(Base).name();
		return ss.str();
	}
};

class B : public Base
{
public:
	using Base::Base;
	~B() { cout << "~B(),"; }

	string get_status() override
	{
		stringstream ss;
		ss << "i = " << i << " : " << typeid(B).name();
		return ss.str();
	}
};

class C : public Base
{
public:
	using Base::Base;
	virtual ~C() { cout << "~C(),"; }

	string get_status() override
	{
		stringstream ss;
		ss << "i = " << i << " : " << typeid(C).name();
		return ss.str();
	}
};

template<typename T>
class MyClass
{
private:
	unique_ptr<T> u_ptr;
	friend MyClass<Base>;
public:
	MyClass(int k)
	{
		u_ptr = make_unique<T>(k);
	}
	template<typename M>
	MyClass(const M& obj)
	{
		u_ptr = make_unique<M>(obj);
	}
	template<typename M>
	MyClass(const M&& obj)
	{
		u_ptr = make_unique<M>(obj);
	}
	template<typename M>
	MyClass(const MyClass<M>& obj)
	{
		u_ptr = make_unique<M>(*obj.u_ptr);
	}
	template<typename M>
	MyClass(MyClass<M>&& obj)
	{
		u_ptr = std::forward<MyClass<M>>(obj).u_ptr;
	}

	void print()
	{
		cout << u_ptr->get_status() << endl;
	}
};

int main()
{
	cout << "--------------------" << endl;
	{
		vector<MyClass<Base>> result;
		{
			vector<MyClass<Base>> v;
			v.reserve(10);

			v.emplace_back(1);
			v.emplace_back<B>(2);
			auto c = C(3);
			v.emplace_back(c);

			v.emplace_back(MyClass<Base>(4));
			v.emplace_back<MyClass<B>>(5);
			auto d = MyClass<C>(6);
			v.emplace_back(d);

			for (auto& obj : v) obj.print();
			std::reverse(begin(v), end(v));
			result = move(v);
		}
		cout << "--------------------" << endl;
		for (auto& obj : result) obj.print();
	}
	cout << "--------------------" << endl;

	return 0;
}
Windows10(VS 2017)の実行結果

コード:

--------------------
~B(),~Base() : i = 2
i = 1 : class Base
i = 2 : class B
i = 3 : class C
i = 4 : class Base
i = 5 : class B
i = 6 : class C
~C(),~Base() : i = 6
~C(),~Base() : i = 3
--------------------
i = 6 : class C
i = 5 : class B
i = 4 : class Base
i = 3 : class C
i = 2 : class B
i = 1 : class Base
~C(),~Base() : i = 6
~B(),~Base() : i = 5
~Base() : i = 4
~C(),~Base() : i = 3
~B(),~Base() : i = 2
~Base() : i = 1
--------------------
Ubuntu16.10(gcc6.2.0)の実行結果

コード:

--------------------
~B(),~Base() : i = 2
i = 1 : 4Base
i = 2 : 1B
i = 3 : 1C
i = 4 : 4Base
i = 5 : 1B
i = 6 : 1C
~C(),~Base() : i = 6
~C(),~Base() : i = 3
--------------------
i = 6 : 1C
i = 5 : 1B
i = 4 : 4Base
i = 3 : 1C
i = 2 : 1B
i = 1 : 4Base
~C(),~Base() : i = 6
~B(),~Base() : i = 5
~Base() : i = 4
~C(),~Base() : i = 3
~B(),~Base() : i = 2
~Base() : i = 1
--------------------
purin52002 さんが書きました: コピー(swapの副産物として)がしたい
でも、複数のクラスとリソースを共有したいわけじゃない
safely derived pointer を使用する必要あるんですかね・・・

アバター
purin52002
記事: 235
登録日時: 3年前
連絡を取る:

Re: [c++]std::unique_ptrをコピーしたい

#8

投稿記事 by purin52002 » 3年前

sleepさん
日本語が下手で申し訳ないです^p^
年々コミュ障レベルが上がるのか会話が下手に、、、

それは置いといて、
テンプレートが出てくるとは思いませんでした。
私が未熟故、正直なんでこれで動くのかがわからないのですが(コンストラクタの中身をおいきれない、、、)
ちゃんと動くらしいし、実行結果まで表示していただいて感謝感謝です。

コードの内容は理解できていませんが、私の求めていた解決策っぽいですし、あとは私がこのレベルまで上り詰めればいいわけですね^p^
、、、どれだけかかるかはわかりませんが^^;

というわけで解決にしたいと思います。
皆さんありがとうございました<(_ _)>
オフトピック
safely derived pointerとは一体、、、?
日本語力だけでなく語彙力も乏しいのでした^^;

ぐぐる先生に聞いたところ、newで作られたポインタらしいですが
読解力も乏しいのでした^p^
よろしければわかりやすくご教授ください<(_ _)>
c++初心者を自負しています。
質問者さんには今後私にプログラミングを教えてくれるようにやさしく丁寧に教えるつもりです。ぎぶあんどていく^p^
回答者さんには精一杯感謝します。ぎぶおんりー^p^

zeek

Re: [c++]std::unique_ptrをコピーしたい

#9

投稿記事 by zeek » 3年前

解決したトピックにコメントしていいのかわかりませんが...

「std::unique_ptrをコピーしたい」というタイトルからして
意味不明な質問だったけど

コード:

struct Base;//純粋仮想クラス
続く説明の「上のコードのように」には、この「純粋仮想クラス」は
含まれておらず、要求内容には含まれていなかったということですね。
勘違いしましたね~

sleep

Re: [c++]std::unique_ptrをコピーしたい

#10

投稿記事 by sleep » 3年前

purin52002 さんが書きました:
オフトピック
safely derived pointerとは一体、、、?
どういうものかは以下を参考にしていただくとして、今回の本題とは若干ズレた話なのでこのトピックでは気にしないでください。
Safely-derived pointers

zeek さんが書きました:

コード:

struct Base;//純粋仮想クラス
続く説明の「上のコードのように」には、この「純粋仮想クラス」は
含まれておらず、要求内容には含まれていなかったということですね。
勘違いしましたね~
多分、純粋仮想クラスの場合でもMyClassのコピーコンストラクタの定義に違いが出ないからではないでしょうか?

コード:

#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <vector>
#include <algorithm>
#include <type_traits>

using namespace std;

class Base
{
public:
	virtual ~Base() = 0;
	virtual string get_status() = 0;
};

inline Base::~Base() {}

class A : public Base
{
protected:
	int i;
public:
	A(int i) : i(i) {}
	A(const A& obj) : i(obj.i) {}
	~A() { cout << "~A(): i = " << i << endl; }

	string get_status() override
	{
		stringstream ss;
		ss << typeid(A).name() << " : " << "i = " << i;
		return ss.str();
	}
};

class B : public Base
{
protected:
	string s;
public:
	B(string s) : s(s) {}
	B(const B& obj) : s(obj.s) {}
	~B() { cout << "~B(): s = " << s << endl; }

	string get_status() override
	{
		stringstream ss;
		ss << typeid(B).name() << " : " << "s = " << s;
		return ss.str();
	}
};

template<typename T>
class MyClass
{
private:
	std::unique_ptr<T> u_ptr;
	friend MyClass<std::common_type_t<Base,T>>;
public:
	MyClass(int i) : u_ptr(make_unique<T>(i)) {}
	MyClass(const char* s) : u_ptr(make_unique<T>(s)) {}
	MyClass(string s) : u_ptr(make_unique<T>(s)) {}
	template<typename M> MyClass(const M& obj) : u_ptr(make_unique<M>(obj)) {}
	template<typename M> MyClass(const M&& obj) : u_ptr(make_unique<M>(obj)) {}
	template<typename M> MyClass(const MyClass<M>& obj) : u_ptr(make_unique<M>(*obj.u_ptr)) {}
	template<typename M> MyClass(MyClass<M>&& obj) : u_ptr(std::forward<MyClass<M>>(obj).u_ptr) {}

	void print() { cout << u_ptr->get_status() << endl; }
};

int main()
{
	cout << "--------------------" << endl;
	{
		vector<MyClass<Base>> result;
		{
			vector<MyClass<Base>> v;
			v.reserve(10);

			//v.emplace_back(1);
			v.emplace_back<A>(2);
			auto c = B("hello");
			v.emplace_back(c);

			//v.emplace_back(MyClass<Base>(4));
			v.emplace_back<MyClass<A>>(5);
			auto d = MyClass<B>("world");
			v.emplace_back(d);

			for (auto& obj : v) obj.print();
			std::reverse(begin(v), end(v));
			result = move(v);
		}
		cout << "--------------------" << endl;
		for (auto& obj : result) obj.print();
	}
	cout << "--------------------" << endl;

	return 0;
}
Windows10(VS 2017)の実行結果

コード:

--------------------
~A(): i = 2
class A : i = 2
class B : s = hello
class A : i = 5
class B : s = world
~B(): s = world
~B(): s = hello
--------------------
class B : s = world
class A : i = 5
class B : s = hello
class A : i = 2
~B(): s = world
~A(): i = 5
~B(): s = hello
~A(): i = 2
--------------------
Ubuntu16.10(gcc6.2.0)の実行結果

コード:

--------------------
~A(): i = 2
1A : i = 2
1B : s = hello
1A : i = 5
1B : s = world
~B(): s = world
~B(): s = hello
--------------------
1B : s = world
1A : i = 5
1B : s = hello
1A : i = 2
~B(): s = world
~A(): i = 5
~B(): s = hello
~A(): i = 2
--------------------

返信

“C言語何でも質問掲示板” へ戻る