1

考慮下面的代碼,其中,相同的附圖被轉發兩次基類,並用於有構造元組:C++多箇中的一個參考轉發:第一拷貝,然後移動

template<typename ... Ts> 
struct Base 
{ 
    template<typename ... _Ts> 
    Base(_Ts&& ... ts) : tup{std::forward<_Ts>(ts) ...} {} 

    std::tuple <Ts ...> tup; 
}; 

template<typename T> 
struct Derived : public Base<T, T> 
{ 
    template<typename _T> 
    Derived(_T&& t) : Base<T, T>{t, std::forward<_T>(t)} {} 
}; 

第一主叫基類的構造在Derived作爲Base<T, T>{t, std::forward<_T>(t)}並且此後也使用tup{std::forward<Ts>(ts)...}元組構造函數有以下原因:

t是一個rvalue參考,第一元組參數應被傳遞的左值-裁判t,從而通過一個被構造警察y的t,而第二個元組元素應該得到一個右值ref,因此,如果可能的話,使用移動來構建。

這種方法似乎得到SO上的幾個問題和答案的支持(例如here,herehere),其中陳述了braced-init列表執行對其參數的從左到右的評估。

然而,當我在一個簡單的例子使用上面的代碼中,實際行爲是(一致)的我的預期相反:

struct A 
{ 
    A() = default; 
    A(A const& other) : vec(other.vec) { std::cout<<"copy"<<std::endl; } 
    A(A && other) : vec(std::move(other.vec)) { std::cout<<"move"<<std::endl; } 

    std::vector<int> vec = std::vector<int>(100); 
}; 

int main() 
{ 
    Derived<A> d(A{}); //prints first "move", then "copy" 

    std::cout<<std::get<0>(d.tup).vec.size()<<std::endl; //prints 0 
    std::cout<<std::get<1>(d.tup).vec.size()<<std::endl; //prints 100 
} 

這裏是the example using gcc on Coliru。 (gcc編譯器顯然有在這方面的錯誤一次,但它是兩年左右,因爲應該沒什麼問題了。)

問題:

  • 我在哪裏錯了就執行或在這裏的假設?
  • 如何修復上述代碼以達到預期效果:第一次複製 - 然後移動?
+2

任何符號開頭一個下劃線後面跟着一個大寫字母,用於執行;把'_T'和'_Ts'改成別的東西。 –

回答

1

我不確定對象初始化的操作順序是否重要。由於完美的轉發,實際上沒有複製或移動(即只傳遞左值引用和右值引用),直到調用構造函數std::tuple。而且,在這一點上,這取決於std::tuple的實施細節。

試想,如果不是的std::tuple您使用以下my_tup結構:

template<typename T1, typename T2> 
struct my_tup 
{ 
    template <typename A, typename B> 
    my_tup(A&& a, B&& b) 
     : t1(std::forward<A>(a)), t2(std::forward<B>(b)) 
    { 
    } 

    T1 t1; 
    T2 t2; 
}; 

這版畫,如預期, 「複製」,然後選擇 「移動」(coliru)。但是,相反,如果你有:

template<typename T1, typename T2> 
struct my_tup 
{ 
    template <typename A, typename B> 
    my_tup(A&& a, B&& b) 
     : t2(std::forward<B>(b)), t1(std::forward<A>(a)) 
    { 
    } 

    T2 t2; 
    T1 t1; 
}; 

那麼這版畫 「移動」,然後選擇 「複製」,爲std::tuple做(coliru)。

可能由於可變參數模板的展開方式,std::tuple必須以從右到左的方式處理參數。我不確定這是依賴於實現還是在規範中指定。

+1

謝謝,我想這就是答案。評估並不意味着建設。標準中相應的元組構造約束似乎不存在。 – davidhigh

+0

另請參閱[這個答案](http://stackoverflow.com/questions/32192809/tuple-isnt-being-constructed-in-order)其中指出,在元組構造中沒有標準順序。 – davidhigh

3

你應該做的一份明確的,否則你通過它後來被複制使用的基準(但,與別名,可能已移動):

template<typename U> 
struct Derived : public Base<U, U> 
{ 
    template<typename T> 
    Derived(T&& t) : Base<U, U>{T(t), std::forward<T>(t)} {} 
}; 

Demo

+0

不錯的技巧:這應該起作用(在將結果傳遞給構造函數之前調用的參數是函數,而std :: forward只是一個強制轉換) –

+0

感謝您的解決方法。 – davidhigh