C++ 如何为具有引用的类模板支持移动语义

C++ 如何为具有引用的类模板支持移动语义,c++,move-semantics,C++,Move Semantics,形势 我正在设计一个支持移动语义的类模板logiclogic有一个模板参数Visitor和一个类型为Visitor&的引用成员。这是一个库代码 用户继承类模板逻辑,并传递自定义访问者,如my\u visitor。自定义访问者可能包含可移动的成员。例如,my_visitor有一个成员v,其类型为std::vector 问题 请参见test2()。当我移动我的_逻辑时,我的_访问者::v会按预期移动。但是,logic::vis指的是从对象移动的对象。有什么好方法可以引用“移动到”对象吗 #inclu

形势

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

用户继承类模板
逻辑
,并传递自定义访问者,如
my\u visitor
。自定义访问者可能包含可移动的成员。例如,
my_visitor
有一个成员
v
,其类型为
std::vector

问题

请参见
test2()
。当我移动我的_逻辑时,
我的_访问者::v
会按预期移动。但是,
logic::vis
指的是从对象移动的对象。有什么好方法可以引用“移动到”对象吗

#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();
}
#包括
#包括
//库代码
模板//概念:访问者应具有访问权限()
结构逻辑{
逻辑(Visitor&v):vis(v){}
void execute(){
vis.visit();
}
//其他API
访客&vis;
//其他成员变量。。。
};
//用户代码
构造我的访客{
我的访客{v.push_-back(42);}
无效访问(){
std::cout使用而不是本机引用:

std::reference\u wrapper
是一个类模板,它将引用封装在可复制、可分配的对象中。它经常用作一种机制,用于将引用存储在标准容器(如
std::vector
)中,而标准容器通常无法保存引用

具体地说,
std::reference_wrapper
是一个可复制且可复制分配的包装器,围绕着对对象的引用或对类型为
T
的函数的引用。
std::reference_wrapper
的实例是对象(它们可以复制或存储在容器中)但是它们可以隐式转换为
T&
,因此它们可以作为参数与通过引用获取底层类型的函数一起使用


您必须编写自己的移动/复制构造函数

struct my_logic : logic<my_visitor> {
    my_logic():logic<my_visitor>(mv) {}
    my_visitor mv;

    my_logic(const my_logic& rhs) : logic<my_visitor>(mv), mv(rhs.mv) {}
    my_logic(my_logic&& rhs) : logic<my_visitor>(mv), mv(std::move(rhs.mv)) {}
};
struct my_逻辑:逻辑{
my_logic():逻辑(mv){}
我的访客mv;
我的逻辑(const我的逻辑&rhs):逻辑(mv),mv(rhs.mv){}
my_logic(my_logic&&rhs):逻辑(mv),mv(std::move(rhs.mv)){
};


使用
reference\u wrapper
,您也可以以类似的方式实现赋值。

问题在于
my\u logic
既有一个成员(
mv
),也有一个对该成员的引用(
vis
)并且您必须确保引用始终引用同一个成员。使用默认的移动构造函数,新引用
vis
仍然引用旧成员,然后旧成员从中移动。这就是为什么您最终位于
0

 m1.mv  <-----+         m2.mv
  ↑           |
  |           |
  |           |
 m1.vis       +------   m2.vis
通过这种方式,只有一种方法可以引用数据-因此没有任何东西会偏离规则

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

logic(logic&& ) = delete;

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

谢谢你的评论。我替换了它们。这是原始代码和被替换的代码如何更新引用包装器vis?@TakatoshiKondo:对我来说似乎是一个不同的问题。我的问题是类模板
logic
如何支持移动语义,以及更新
vis
引用移动到对象。我想不是,而是你无论如何都需要它,否则访问者可能不会被分配到。我认为我可以使用本机引用实现移动分配运算符。这有点棘手。请参阅第16-20行您必须为
my\u logic
实现移动构造函数(并可能删除移动分配).my_logic
的移动构造函数似乎创建了一个新的
逻辑
。类模板逻辑还有其他成员变量(我写了一条注释)。这是演示中的第18行。我想移动所有成员变量。我刚刚将演示代码更新为。我为
逻辑
逻辑添加了一个构造函数(逻辑和其他,访问者和v):vis(v),s(std::move(other.s)){}
(第9行)和调用代码my_逻辑(my_逻辑和rhs):逻辑(std::move(rhs),mv,mv(std::move(rhs.mv)){}(第49行)。然后它就如预期的那样工作了。谢谢。我意识到我不能用这种方式编写移动赋值运算符。因为它需要两个参数。似乎我需要为此编写一个不同的成员函数……谢谢你的回答。它工作得很好!我改变了
逻辑
的模板参数的概念。VisitorHolder应该ve函数
visit()
返回具有
visit()
成员函数的访问者。我将代码更新为。我还支持不带引用包装器的移动分配为。我复制/粘贴了移动分配版本的URL是错误的。正确的是。
logic(logic&& ) = delete;

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