2016-04-23 50 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

我意識到我不能使用這種方式編寫移動賦值運算符。因爲它需要兩個參數。似乎我需要爲此寫一個不同的成員函數... –