2016-04-23 65 views
2

形势如何支持具有参考

我设计,支持移动语义类模板logic类模板移动语义。 logic具有模板参数Visitor,并且类型为Visitor&的参考成员。这是一个库代码。

用户继承类模板logic并传递一个自定义访问者,如my_visitor。自定义访问者可能包含可移动成员。例如,my_visitor具有构件v这类型是std::vector

问题

test2()。当我移动my_logic时,my_visitor::v按预期移动。但是,logic<Visitor>::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(); 
} 
+0

您必须实现'my_logic'的移动构造函数(并可能删除移动赋值)。 – Jarod42

回答

1

问题是my_logic同时拥有一个成员(mv)和对该成员的引用(vis),并且您必须确保该引用始终引用同一成员。随着违约移动构造函数,新的参考vis还是指成员,然后把它从移动。这就是为什么你在0结束:

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

一种解决方案是,为Jarod建议,编写自己的复制/移动构造函数/赋值运算符,以确保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;  
}; 

这样的话,只有一个引用资料的方式 - 所以没有什么可以脱节。

或者,您可以明确delete复制/移动构造函数和赋值运算符logic。这将要求您明确地为所有派生类型编写自己的类型,但会确保您正确地完成了它。例如:

logic(logic&&) = delete; 

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

谢谢你的答案。它完美的工作!我改变了逻辑模板参数的概念。 VisitorHolder应具有返回具有visit()成员函数的访问者的visit()函数。 我将我的代码更新为http://melpon.org/wandbox/permlink/SvnLl0sR4JrOwMMX。我还支持不带引用包装的移动作业,如http://melpon.org/wandbox/permlink/xPFwfOuVavxmhcpw。 –

+0

我为移动任务版本复制/粘贴的URL是错误的。一个是http://melpon.org/wandbox/permlink/L3I5A9zWhj2QM7UN。 –

1

使用std::reference_wrapper而不是本机参考:

std::reference_wrapper是一个模板类,它包装在一个可复制,可分配的对象的引用。它经常被用作一种机制来将参考文件存储在标准容器(如std::vector)中,这些标准容器通常无法保存参考。

具体地说,std::reference_wrapper复制构造CopyAssignable包装纸围绕参考到对象或参考T类型的功能。 std::reference_wrapper的实例是对象(它们可以被复制或存储在容器中),但它们可以隐式转换为T&,以便它们可以用作参考引用基础类型的函数的参数。

+0

感谢您的评论。我替换了它们。这里是原始代码http://melpon.org/wandbox/permlink/jbp876DbGDsYg6Q1和替换代码http://melpon.org/wandbox/permlink/x1QbEaOgDHmeaxjz 如何更新引用包装vis? –

+1

@TakatoshiKondo:对我来说看起来是另一回事。 –

+0

我的问题是如何类模板'逻辑'支持移动语义与更新'vis'指移动到对象。 –

0

你必须写你自己的移动/复制构造类似的方式

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

Demo

并与reference_wrapper,你也可以实现分配。

+0

似乎'my_logic'的移动构造函数创建了一个新的逻辑''。类模板逻辑有其他成员变量(我写了评论)。它是你演示中的第18行。我想移动所有成员变量。我只是将你的演示代码更新为http://coliru.stacked-crooked.com/a/aa6ce7e2506423eb。 (逻辑 &&其他,Visitor&v):vis(v),s(std :: move(other.s)){}'''(第9行)和'逻辑'(逻辑 && other,调用代码my_logic(my_logic && rhs):逻辑(std :: move(rhs),mv),mv(std :: move(rhs.mv)){}(第49行)。然后它按预期工作。谢谢。 –

+0

我意识到我不能使用这种方式编写移动赋值运算符。因为它需要两个参数。似乎我需要为此写一个不同的成员函数... –