c++ - 如何支持具有引用的类模板的 move 语义

标签 c++ move-semantics

情况

我正在设计一个类模板logic支持 move 语义。 logic有一个模板参数Visitor类型为 Visitor& 的引用成员。这是一个库代码。

用户继承类模板logic并传递自定义访问者,例如 my_visitor 。自定义访问者可能包含可 move 成员。例如,my_visitor已有成员(member)v其类型为std::vector .

问题

参见test2() 。当我 move my_logic 时,my_visitor::v已按预期 move 。然而,logic<Visitor>::vis指的是被 move 的对象。有什么好的方法来引用 move 到的对象吗?

#include <iostream>
#include <vector>

// Library code
template <typename Visitor> // Concept: Visitor should have visit() 
struct logic {
    logic(Visitor& v):vis(v) {}
    void execute() {
        vis.visit();
    }
    // Other APIs

    Visitor& vis;
    // Other member variables...
};

// User code

struct my_visitor {
    my_visitor() { v.push_back(42); }
    void visit() {
        std::cout << "expected 1, actual " << v.size() << std::endl;
    }
    std::vector<int> v;
};

// User inherits all logic's APIs 
struct my_logic : logic<my_visitor> {
    my_logic():logic<my_visitor>(mv) {}
    my_visitor mv;
};

void test1() {
    std::cout << "test1" << std::endl;
    my_logic m;
    m.execute();
}

void test2() {
    std::cout << "test2" << std::endl;
    my_logic m1;
    {
        my_logic m2(std::move(m1)); // logic::vis refers to moved from my_visitor...
        m2.execute();
    }
}


int main() {
    test1();
    test2();
}

最佳答案

问题是 my_logic 既有成员 (mv) 又有对该成员的引用 (vis),您必须确保该引用始终指的是同一成员。使用默认的 move 构造函数,新引用 vis 仍然引用旧成员,然后将其移出。这就是为什么你最终会得到0:

 m1.mv  <-----+         m2.mv
  ↑           |
  |           |
  |           |
 m1.vis       +------   m2.vis

一种解决方案是,如 Jarod建议编写您自己的复制/move 构造函数/赋值运算符,以确保 m2.vis 指向 m2.mv

但是,我建议通过使用 CRTP 并让基本 logic 类直接引用派生类来避免额外的引用:

template <class Derived>
struct logic {
    Derived& self() { return static_cast<Derived&>(*this); }

    void execute() {
        self().visit();
    }
};

struct my_visitor : logic<my_visitor) {
    my_visitor() { v.push_back(42); }
    void visit() {
        std::cout << "expected 1, actual " << v.size() << std::endl;
    }
    std::vector<int> v;       
};

这样,就只有一种方法可以引用数据 - 因此不会出现任何问题。

或者,您可以显式删除逻辑的复制/move 构造函数和赋值运算符。这将要求您为所有派生类型显式编写自己的类型,但会确保您做得正确。例如:

logic(logic&& ) = delete;

my_logic(my_logic&& rhs)
: logic(mv) // always refer to me!
, mv(std::move(rhs.mv))
{ } 

关于c++ - 如何支持具有引用的类模板的 move 语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36810759/

相关文章:

c++ - 在破坏的对象上 move 构造函数?

c++ - 三元运算符 ? : vs if. ..否则

c++ - 使用 auto : does not name a type, c++ 版本的 numpy arange 时出错

c++ - 在 C++ 中存储一组坐标集(成对 vector 的 vector ?)

rust - 整个Box都可以 move 时,Box可以 move 其内容吗?

c++ - 为什么在 std::vector 的初始化列表中调用复制构造函数?

c++ - 处理许多自定义异常的最佳方法是什么

c++ - 在 C++ 中将 MapB 同步到 MapA

c++ - 交换对象不实现 move 语义时的交换函数

c++ - initializer_list 和 move 语义