2017-07-16 76 views
0

考慮下面的代碼:問題努力構建'的std :: VECTOR`與初始化列表

#include <memory> 
#include <vector> 

class A { 
public: 
    explicit A(std::vector<int> &&v) : v_(std::move(v)) {} 

private: 
    std::vector<int> v_; 
}; 

int main() { 
    // compilation error (no matching call to std::make_unique) 
    // compiler output: https://ideone.com/4oKjCS 
    std::vector<std::unique_ptr<A>> as1 = {std::make_unique<A>({1}), 
             std::make_unique<A>({2})}; 

    // compilation error (requested copy of std::unique_ptr) 
    // compiler output: https://ideone.com/5LGPoa 
    std::vector<std::unique_ptr<A>> as2 = { 
     std::make_unique<A>(std::vector<int>({1})), 
     std::make_unique<A>(std::vector<int>({2}))}; 

    // succeeds 
    std::vector<std::unique_ptr<A>> as3; 
    as3.push_back(std::make_unique<A>(std::vector<int>({1}))); 
    as3.push_back(std::make_unique<A>(std::vector<int>({2}))); 
} 
  • 對於as1:我希望std::make_unique<A>({1})調用std::vector隱含的初始化列表構造,然後傳遞矢量到std::make_unique。爲什麼不編譯?
  • 對於as2std::make_unique的結果是一個右值。爲什麼要在任何地方提交副本?
  • 有沒有比我的as3更有地道或更短的方式來完成這項工作?

編輯:我現在記得as1中的錯誤原因。 Meyers'Effective Modern C++在第30條中提到了初始值設定項列表作爲完美轉發的失敗情況之一:「將標準初始化程序傳遞給函數模板參數(未聲明爲std::initializer_list)被規定爲,如標準所示,「未推斷的上下文」。「

回答

2

問題是std::unique_ptr,而不是std::initializer_liststd::initializer_list的值通過臨時緩衝區複製到目標對象。 unique_ptr不可複製。您需要以其他方式對其進行初始化,可能通過reserve()/emplace_back()

對不起,我知道這聽起來令人氣憤,但確實有沒有爲此目的使用初始化列表的好方法。

以下示例顯示如何將初始化指針的臨時向量與初始化程序列表一起使用。這個例子並不漂亮,我不會推薦它用於任何真正的代碼,但是如果你在初始化列表中設置,它將與std::unique_ptr一起工作,並且只要構造函數不拋出就不會引入內存泄漏。

#include <memory> 
#include <vector> 


int main(void) 
{ 
    std::vector<int*> v = { 
     new int(1), 
     new int(2), 
     new int(3), 
     new int(4), 
    }; 

    std::vector<std::unique_ptr<int>> v1(v.begin(), v.end()); 

    return 0; 
} 

相反,我會推薦一些更類似於原來的例如:使用reserve/emplace_back()。也許稍微冗長些,但意圖是清楚的,而且語法更具慣用性。

std::vector<std::unique_ptr<int>> v; 
v.reserve(50); 
for (size_t i = 0; i < 50; ++i) { 
    v.emplace_back(std::make_unique<int>(i)); 
} 

後者與可能拋出,如亨利在評論中指出,建設者的記憶,只剩安全解決方案。您應該在所有實際代碼中使用後一個示例。

+1

「int」的例子在任何情況下都可以正常工作,因爲'new int'只能拋出'bad_alloc',從中無法恢復。自定義數據類型的構造函數可能會拋出更多的問題,因爲那樣你會從原始指針的部分初始化向量中泄漏內存。 'emplace_back'解決方案是唯一的故障安全解決方案。 (1) –

3

as1

使獨特的用途「完美的轉發」。完美的轉發是不完美的,並不能很好地支持初始化列表。

AS2

初始化列表是(雙)指針的自動存儲持續時間const陣列。 const對象不能從中移出,而是從中複製。你不能複製獨特的ptrs。

AS3

template<class T, class...Ts> 
std::vector<T> make_vector(Ts&&...ts){ 
    std::array<T,sizeof...(ts)> tmp={{std::forward<Ts>(ts)...}}; 
    std::vsctor<T> r{ 
    std::make_move_iterator(begin(tmp)), 
    std::make_move_iterator(end(tmp)) 
    }; 
} 

給予我們:

auto as4=make_vector<std::unique_ptr<A>>(
    std::make_unique<A>(make_vector<int>(1)), 
    std::make_unique<A>(make_vector<int>(2)) 
); 

這可能不是很理想,但都是圍繞載體瘦包裝對象的唯一PTR是一個壞主意。

在更復雜的情況下,直接創建唯一A的幫助函數會削減樣板。