C++ 移动赋值运算符与虚拟继承
类似于我的问题已经在这个社区讨论过了(有好几个帖子,比如,、和),但最有趣的一个(我想在这里讨论)是,尽管它并不能真正解决我的问题。我想讨论的是以下警告:C++ 移动赋值运算符与虚拟继承,c++,inheritance,multiple-inheritance,move-semantics,C++,Inheritance,Multiple Inheritance,Move Semantics,类似于我的问题已经在这个社区讨论过了(有好几个帖子,比如,、和),但最有趣的一个(我想在这里讨论)是,尽管它并不能真正解决我的问题。我想讨论的是以下警告: warning: defaulted move assignment for ‘UG’ calls a non-trivial move assignment operator for virtual base ‘G’. 在最后提到的帖子中,回答说这个警告是说基类可以移动两次,等等 第二个移动指定来自已从对象移动的对象,该对象 可能导致第一
warning: defaulted move assignment for ‘UG’ calls a non-trivial move assignment operator for virtual base ‘G’.
在最后提到的帖子中,回答说这个警告是说基类可以移动两次,等等
第二个移动指定来自已从对象移动的对象,该对象
可能导致第一次移动分配的内容被删除
覆盖
我明白这是一个问题,最好避免。现在,我有几个从纯虚拟基类继承的类。多重继承也涉及其中,如下面的MWE所示。我想要的是在需要时使用move构造函数和move赋值操作符的可能性,这样我就可以
T t3;
T t2 = std::move(t1);
t3 = std::move(t2);
不用担心内存泄漏,也不用担心所有内容都被正确移动。目前,t2=std::move(t1)代码>工作正常,但t3=std::move(t2)代码>没有。我制作了一个MWE,它很好地代表了我的实际代码,我非常相信MWE的解决方案也将是我的代码的解决方案。MWE是:
class G {
public:
G() = default;
G(G&&) = default;
G(const G&) = default;
virtual ~G() = default;
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
return *this;
}
G& operator= (const G&) = default;
virtual void asdf() = 0; // abstract function to force complexity
string mem_G;
};
class UG : virtual public G {
public:
UG() = default;
UG(UG&& u) = default;
UG(const UG&) = default;
virtual ~UG() = default;
UG& operator= (UG&&) = default;
UG& operator= (const UG&) = default;
void asdf() { mem_G = "asdf"; }
string mem_UG;
};
class T : virtual public G {
public:
T() = default;
T(T&& t) = default;
T(const T&) = default;
virtual ~T() = default;
T& operator= (T&&) = default;
T& operator= (const T&) = default;
virtual void qwer() = 0;
string mem_T;
};
class FT : public UG, virtual public T {
public:
FT() = default;
FT(FT&& f) = default;
FT(const FT&) = default;
virtual ~FT() = default;
FT& operator= (FT&&) = default;
FT& operator= (const FT&) = default;
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
};
注意mem\u G
在最后一次出现时是如何将其值保留在c2
中的。如果我默认G&operator=(G&&&)
而不是定义它,那么结果只在该行中不同:
c2
mem_G: // this memory has been moved twice
问题如何在该继承结构中实现移动赋值运算符(以及移动构造函数,如果需要的话),以便两者只移动内存一次?是否可能在没有上述警告的情况下使用此类代码
提前谢谢
编辑感谢您的回答,此问题已得到解决。我想人们会发现看到一个完整的解决方案方案是很有用的,所以我添加了一个扩展版的MWE,增加了两个类,这样它就有点复杂了。此外,还有main
函数,因此可以测试类。最后,我想补充一点,valgrind在执行代码的调试编译时不会抱怨内存泄漏
Edit我按照5的规则完成了这个示例,就像一位对此答案发表评论的用户指出的那样,我想我会更新答案。代码编译时没有警告标志-Wall-Wpedantic-Wshadow-Wextra-Wconversion-Wold-style cast-verterprict-Wduplicated cond-Wnon virtual dtor-Woverloaded virtual
,使用valgrind
执行不会产生任何错误。我还使用\uu PRETTY\u FUNCTION\uuu
宏添加了cout
s,以便任何希望测试代码的人都可以看到函数调用的跟踪
#include <functional>
#include <iostream>
#include <string>
using namespace std;
class G {
public:
G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = "empty";
}
G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
}
G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
}
virtual ~G() { }
G& operator= (const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
return *this;
}
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
return *this;
}
friend ostream& operator<< (ostream& os, const G& r) {
os << " mem_G: " << r.mem_G;
return os;
}
virtual void asdf() = 0;
string mem_G;
protected:
void copy_full_G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = g.mem_G;
}
void move_full_G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = std::move(g.mem_G);
}
};
class UG : virtual public G {
public:
UG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_UG = "empty";
}
UG(const UG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
}
UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
}
virtual ~UG() { }
UG& operator= (const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
return *this;
}
UG& operator= (UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const UG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_UG;
protected:
void copy_full_UG(const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_UG = u.mem_UG;
}
void move_full_UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_UG = std::move(u.mem_UG);
}
};
class DG : virtual public G {
public:
DG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_DG = "empty";
}
DG(const DG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
}
DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
}
virtual ~DG() { }
DG& operator= (const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
return *this;
}
DG& operator= (DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const DG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_DG;
protected:
void copy_full_DG(const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_DG = u.mem_DG;
}
void move_full_DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_DG = std::move(u.mem_DG);
}
};
class T : virtual public G {
public:
T() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = "empty";
}
T(const T& t) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
}
T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
}
virtual ~T() { }
T& operator= (const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
return *this;
}
T& operator= (T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
return *this;
}
friend ostream& operator<< (ostream& os, const T& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_T: " << r.mem_T;
return os;
}
virtual void qwer() = 0;
string mem_T;
protected:
// Copy *only* T members.
void copy_only_T(const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = t.mem_T;
}
// Move *only* T members.
void move_only_T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
// if we moved G's members too then we
// would be moving G's members twice!
//move_full_G(std::move(static_cast<G&>(t)));
mem_T = std::move(t.mem_T);
}
};
class FT : public UG, virtual public T {
public:
FT() : T(), UG(){
cout << __PRETTY_FUNCTION__ << endl;
mem_FT = "empty";
}
FT(const FT& f) : G(), T(), UG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
}
FT(FT&& f) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_FT(std::move(static_cast<FT&>(f)));
}
virtual ~FT() { }
FT& operator= (const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
return *this;
}
FT& operator= (FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign FT members
move_full_FT(std::move(static_cast<FT&>(other)));
return *this;
}
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
protected:
void copy_full_FT(const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(f);
copy_only_T(f);
mem_FT = f.mem_FT;
}
void move_full_FT(FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign UG members and also the base class's members
move_full_UG(std::move(static_cast<UG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_FT = std::move(other.mem_FT);
}
};
class RT : public DG, virtual public T {
public:
RT() : T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
mem_RT = "empty";
}
RT(const RT& f) : G(), T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(f);
}
RT(RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_RT(std::move(static_cast<RT&>(r)));
}
virtual ~RT() { }
RT& operator= (const RT& r) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(r);
return *this;
}
RT& operator= (RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign RT members
move_full_RT(std::move(static_cast<RT&>(r)));
return *this;
}
friend ostream& operator<< (ostream& os, const RT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_RT: " << r.mem_RT;
return os;
}
void qwer() { mem_RT = "zxvc"; }
string mem_RT;
protected:
void copy_full_RT(const RT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(f);
copy_only_T(f);
mem_RT = f.mem_RT;
}
void move_full_RT(RT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign DG members and also the base class's members
move_full_DG(std::move(static_cast<DG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_RT = std::move(other.mem_RT);
}
};
template<class C> void test_move(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
init_C(c1);
cout << "Initialise c1" << endl;
cout << c1 << endl;
cout << "Move constructor: 'c2 <- c1'" << endl;
C c2 = std::move(c1);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Move assignment operator: 'c1 <- c2'" << endl;
c1 = std::move(c2);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C> void test_copy(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
cout << "Initialise c1" << endl;
init_C(c1);
cout << c1 << endl;
cout << "Copy constructor: 'c2 <- c1'" << endl;
C c2 = c1;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Copy assignment operator: 'c1 <- c2'" << endl;
c1 = c2;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C>
void test(const string& what, const function<void (C&)>& init_C) {
cout << "********" << endl;
cout << "** " << what << " **" << endl;
cout << "********" << endl;
cout << "----------" << endl;
cout << "-- MOVE --" << endl;
cout << "----------" << endl;
test_move<C>(init_C);
cout << "----------" << endl;
cout << "-- COPY --" << endl;
cout << "----------" << endl;
test_copy<C>(init_C);
}
int main() {
test<UG>(
"UG",
[](UG& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
}
);
test<DG>(
"DG",
[](DG& d) -> void {
d.mem_G = "I am G";
d.mem_DG = "I am DG";
}
);
test<FT>(
"FT",
[](FT& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
u.mem_T = "I am T";
u.mem_FT = "I am FT";
}
);
test<RT>(
"RT",
[](RT& u) -> void {
u.mem_G = "I am G";
u.mem_DG = "I am DG";
u.mem_T = "I am T";
u.mem_RT = "I am RT";
}
);
}
#包括
#包括
#包括
使用名称空间std;
G类{
公众:
G(){
cout问题在于FT
的FT&operator=(FT&&)=default;
基本上是:
FT&operator=(FT&other){
//移动指定基类
static_cast(*this)=std::move(static_cast(other));//同时move分配G
//other.mem_G在移动后现在为空
static_cast(*this)=std::move(static_cast(other));//同时move分配G
//此->内存现在为空
//移动和分配成员
mem_FT=std::move(other.mem_FT);
}
(虽然不完全如此。编译器被允许是智能的,并且只从虚拟基类移动一次,但至少在gcc和clang中不会发生这种情况)
其中单个基类子对象G
从other
移动两次(通过两个移动赋值)。但是other.mem\u G
在第一次移动后为空,因此在移动赋值后为空
处理此问题的方法是确保虚拟基地只分配一次移动。这可以通过编写以下内容轻松完成:
FT&operator=(FT&other)无例外{
//同时移动指定的'G'`
静态施法(*此)=标准::移动(静态施法(其他));
//移动分配UG成员而不移动移动`G的UG移动分配`
mem_UG=std::move(other.mem_UG);
//移动和分配FT成员
mem_FT=std::move(other.mem_FT);
}
对于私有成员或更复杂的移动分配,您可能希望使用受保护的move\u only\u my\u members\u from\u this\u type\u和\u not\u virtual\u base(UG&)
member函数
您还可以通过不生成默认的移动赋值运算符来解决此问题,使基类复制两次而不是变为空,以获得潜在的性能影响。我坚信,当您有虚拟移动构造函数时,五法则肯定适用。最好在这里显式。这实际上是警告试图告诉您的you@SebastianHoffmann什么是“虚拟移动构造函数”?@aschepler Sorry,我是说虚拟移动assignment@SebastianHoffmann这里没有任何虚拟赋值函数,但是,是的,如果有,事情会变得更混乱。好的,所以我们的想法是实现这些操作符。另一个用户发布了一个答案,其中展示了如何在没有内存泄漏的情况下实际执行,所以我接受这一点作为ans欢迎发帖。无论如何,谢谢大家。还有@Sebastianhofmann,谢谢你们提到五的规则;我不知道。谢谢你们!你们的建议对MWE很有效,我没有理由认为它不适用于我的实际代码。我会接受你们的答案作为正确答案,但我也会用更全面的MWE智慧发布一个答案h完整的代码,所以人们有一个完整的指南。我希望我没有过火:)再次感谢你花时间回答我的问题。我想我会编辑帖子,而不是发布答案。。。
c2
mem_G: // this memory has been moved twice
#include <functional>
#include <iostream>
#include <string>
using namespace std;
class G {
public:
G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = "empty";
}
G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
}
G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
}
virtual ~G() { }
G& operator= (const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
return *this;
}
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
return *this;
}
friend ostream& operator<< (ostream& os, const G& r) {
os << " mem_G: " << r.mem_G;
return os;
}
virtual void asdf() = 0;
string mem_G;
protected:
void copy_full_G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = g.mem_G;
}
void move_full_G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = std::move(g.mem_G);
}
};
class UG : virtual public G {
public:
UG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_UG = "empty";
}
UG(const UG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
}
UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
}
virtual ~UG() { }
UG& operator= (const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
return *this;
}
UG& operator= (UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const UG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_UG;
protected:
void copy_full_UG(const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_UG = u.mem_UG;
}
void move_full_UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_UG = std::move(u.mem_UG);
}
};
class DG : virtual public G {
public:
DG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_DG = "empty";
}
DG(const DG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
}
DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
}
virtual ~DG() { }
DG& operator= (const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
return *this;
}
DG& operator= (DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const DG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_DG;
protected:
void copy_full_DG(const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_DG = u.mem_DG;
}
void move_full_DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_DG = std::move(u.mem_DG);
}
};
class T : virtual public G {
public:
T() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = "empty";
}
T(const T& t) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
}
T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
}
virtual ~T() { }
T& operator= (const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
return *this;
}
T& operator= (T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
return *this;
}
friend ostream& operator<< (ostream& os, const T& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_T: " << r.mem_T;
return os;
}
virtual void qwer() = 0;
string mem_T;
protected:
// Copy *only* T members.
void copy_only_T(const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = t.mem_T;
}
// Move *only* T members.
void move_only_T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
// if we moved G's members too then we
// would be moving G's members twice!
//move_full_G(std::move(static_cast<G&>(t)));
mem_T = std::move(t.mem_T);
}
};
class FT : public UG, virtual public T {
public:
FT() : T(), UG(){
cout << __PRETTY_FUNCTION__ << endl;
mem_FT = "empty";
}
FT(const FT& f) : G(), T(), UG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
}
FT(FT&& f) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_FT(std::move(static_cast<FT&>(f)));
}
virtual ~FT() { }
FT& operator= (const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
return *this;
}
FT& operator= (FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign FT members
move_full_FT(std::move(static_cast<FT&>(other)));
return *this;
}
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
protected:
void copy_full_FT(const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(f);
copy_only_T(f);
mem_FT = f.mem_FT;
}
void move_full_FT(FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign UG members and also the base class's members
move_full_UG(std::move(static_cast<UG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_FT = std::move(other.mem_FT);
}
};
class RT : public DG, virtual public T {
public:
RT() : T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
mem_RT = "empty";
}
RT(const RT& f) : G(), T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(f);
}
RT(RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_RT(std::move(static_cast<RT&>(r)));
}
virtual ~RT() { }
RT& operator= (const RT& r) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(r);
return *this;
}
RT& operator= (RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign RT members
move_full_RT(std::move(static_cast<RT&>(r)));
return *this;
}
friend ostream& operator<< (ostream& os, const RT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_RT: " << r.mem_RT;
return os;
}
void qwer() { mem_RT = "zxvc"; }
string mem_RT;
protected:
void copy_full_RT(const RT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(f);
copy_only_T(f);
mem_RT = f.mem_RT;
}
void move_full_RT(RT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign DG members and also the base class's members
move_full_DG(std::move(static_cast<DG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_RT = std::move(other.mem_RT);
}
};
template<class C> void test_move(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
init_C(c1);
cout << "Initialise c1" << endl;
cout << c1 << endl;
cout << "Move constructor: 'c2 <- c1'" << endl;
C c2 = std::move(c1);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Move assignment operator: 'c1 <- c2'" << endl;
c1 = std::move(c2);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C> void test_copy(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
cout << "Initialise c1" << endl;
init_C(c1);
cout << c1 << endl;
cout << "Copy constructor: 'c2 <- c1'" << endl;
C c2 = c1;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Copy assignment operator: 'c1 <- c2'" << endl;
c1 = c2;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C>
void test(const string& what, const function<void (C&)>& init_C) {
cout << "********" << endl;
cout << "** " << what << " **" << endl;
cout << "********" << endl;
cout << "----------" << endl;
cout << "-- MOVE --" << endl;
cout << "----------" << endl;
test_move<C>(init_C);
cout << "----------" << endl;
cout << "-- COPY --" << endl;
cout << "----------" << endl;
test_copy<C>(init_C);
}
int main() {
test<UG>(
"UG",
[](UG& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
}
);
test<DG>(
"DG",
[](DG& d) -> void {
d.mem_G = "I am G";
d.mem_DG = "I am DG";
}
);
test<FT>(
"FT",
[](FT& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
u.mem_T = "I am T";
u.mem_FT = "I am FT";
}
);
test<RT>(
"RT",
[](RT& u) -> void {
u.mem_G = "I am G";
u.mem_DG = "I am DG";
u.mem_T = "I am T";
u.mem_RT = "I am RT";
}
);
}