ページ 1 / 1
[c++]std::unique_ptrをコピーしたい
Posted: 2017年4月04日(火) 19:02
by purin52002
こんにちは、今回は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の中身はどうなっても構いません。
よろしくお願いします<(_ _)>
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月07日(金) 21:14
by zeek
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 &); // 代入演算子禁止
};
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月07日(金) 22:13
by purin52002
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
引き続きよろしくお願いします。<(_ _)>
オフトピック
お返事本当にありがとうございました。
このまま埋もれてしまうのでは、、、と若干不安だったのです^^;
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月08日(土) 14:51
by zeek
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;
}
}
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月08日(土) 15:48
by YuO
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の方が適当なことも多いかと思います。
どういう要件になっているかは,各関数の要件を見るしかないのですが……。
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月08日(土) 23:01
by purin52002
zeekさん
その発想はありませんでした。
decltypeを使って何とかできないかな、と試行錯誤したことはあったんですが(結果失敗^^;)
全派生クラスを直書きするという発想はありませんでした。
ただ、やはり汎用性というか、後から派生クラスを追加することも考えて出来れば直書きしない方法があればなー、と高望みしたりしなかったり、、、^^;
YuOさん
swapの特殊化とは、、、俺に考え付かないことを平然と思いつくッ!
しかし、そうですか、、、swapを使わないこともあるのですか、んー、、、
shared_ptrを使うことも検討したのですが、
コピー(swapの副産物として)がしたい
でも、複数のクラスとリソースを共有したいわけじゃない
という理由で、shared_ptrを使うのは適当じゃないかなーと思いまして^^;
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月09日(日) 18:36
by sleep
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 を使用する必要あるんですかね・・・
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月10日(月) 00:17
by purin52002
sleepさん
日本語が下手で申し訳ないです^p^
年々コミュ障レベルが上がるのか会話が下手に、、、
それは置いといて、
テンプレートが出てくるとは思いませんでした。
私が未熟故、正直なんでこれで動くのかがわからないのですが(コンストラクタの中身をおいきれない、、、)
ちゃんと動くらしいし、実行結果まで表示していただいて感謝感謝です。
コードの内容は理解できていませんが、私の求めていた解決策っぽいですし、あとは私がこのレベルまで上り詰めればいいわけですね^p^
、、、どれだけかかるかはわかりませんが^^;
というわけで解決にしたいと思います。
皆さんありがとうございました<(_ _)>
オフトピック
safely derived pointerとは一体、、、?
日本語力だけでなく語彙力も乏しいのでした^^;
ぐぐる先生に聞いたところ、newで作られたポインタらしいですが
読解力も乏しいのでした^p^
よろしければわかりやすくご教授ください<(_ _)>
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月12日(水) 01:11
by zeek
解決したトピックにコメントしていいのかわかりませんが...
「std::unique_ptrをコピーしたい」というタイトルからして
意味不明な質問だったけど
コード:
struct Base;//純粋仮想クラス
続く説明の「上のコードのように」には、この「純粋仮想クラス」は
含まれておらず、要求内容には含まれていなかったということですね。
勘違いしましたね~
Re: [c++]std::unique_ptrをコピーしたい
Posted: 2017年4月12日(水) 19:02
by sleep
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
--------------------