Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 移动赋值运算符与虚拟继承_C++_Inheritance_Multiple Inheritance_Move Semantics - Fatal编程技术网

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";
    }
    );
}