2014-06-15 40 views
14
#include <iostream> 
#include <memory> 

class Base 
{ 
public: 
    Base() {} 
}; 

class Derived : public Base 
{ 
public: 
    Derived() {} 
    Derived(std::initializer_list<std::pair<int, std::shared_ptr<Base>>>) {} 
}; 

int main(int argc, char ** argv) 
{ 
    auto example = new Derived({ 
     { 0, std::make_shared<Derived>() } 
    }); 

    return 0; 
} 

它的工作原理(live preview)make_shared正常,但是當我嘗試使用std::make_sharedstd::initializer_list作爲參數,我得到了錯誤:的std ::用的std :: initializer_list

auto example = new Derived({ 
    { 0, std::make_shared<Derived>({ 
     { 0, std::make_shared<Derived>() } 
    }) } 
}); 

正如你可以在這裏看到在live preview

error: too many arguments to function...

當我做它的工作只有這個(live preview):

auto example = new Derived({ 
    { 0, std::make_shared<Derived>(std::initializer_list<std::pair<int, std::shared_ptr<Base>>> { 
     { 0, std::make_shared<Derived>() } 
    }) } 
}); 

我想知道的是:爲什麼它的工作原理,只有當我通過std::initializer_list作爲參數上std::make_shared,而不是使用{{}}剛像這樣:

auto example = new Derived({ { 0, std::make_shared<Base>() } }); 

難道可以讓std::make_shared接受嗎?

在此先感謝。

+8

支撐初始值設定項不是表達式,也沒有類型。因此,'make_shared'的模板類型推導不能推導出它。 – dyp

回答

4

爲此,您需要創建自定義make_shared_from_list,因爲make_shared不支持非顯式初始值設定項列表。理由被@brian描述得很好。

我會使用traits類將類型T映射到初始值設定項列表的類型。

template<class>struct list_init{};// sfinae support 
template<> struct list_init<Derived>{using type=std::pair<int, std::shared_ptr<Base>>;}; 

template<class T>using list_init_t=typename list_init<T>::type; 

template<class T> 
std::shared_ptr<T> make_shared_from_list(std::initializer_list<list_init_t<T>> list){ 
    return std::make_shared<T>(std::move(list)); 
} 

或類似的東西。

或者,「cast」{...}initializer_list<blah>可以直接(不是演員,而是一個結構)工作。

理論上,足夠的反射元編程支持將允許shared_ptr在沒有traits類的情況下執行此操作,該位非常靠近管道。

13

之所以

auto example = new Derived({ 
    { 0, std::make_shared<Derived>() } 
}); 

作品是編譯器知道它必須與構造

Derived::Derived(std::initializer_list<std::pair<int, std::shared_ptr<Base>>>) {} 

所以很顯然,

{{ 0, std::make_shared<Derived>() }} 

某種方式匹配的初始化初始化程序列表的元素,

{ 0, std::make_shared<Derived>() } 

需要用來初始化std::pair<int, std::shared_ptr<Base>>。然後它找到一個構造爲一對帶有兩個元件,

pair::pair (const first_type& a, const second_type& b); 

其中first_typeintsecond_typestd::shared_ptr<Base>。所以最後我們看到參數std::make_shared<Derived>()被隱式轉換爲std::shared_ptr<Base>,我們很好走!

在上面,我指出編譯器通過尋找一個構造函數來處理初始化列表,該構造函數直接接受初始化列表,或者適當數量的參數,初始化列表元素在適當的隱式必要時進行轉換。例如,編譯器可以發現在上面的例子中你的std::shared_ptr<Derived>需要被隱式轉換爲std::shared_ptr<Base>,這是因爲這個對的構造函數需要它。

現在考慮

std::make_shared<Derived>({ 
     { 0, std::make_shared<Derived>() } 
    }) 

的問題是,make_shared<Derived>是部分特殊函數模板可以接受任意數量和類型的參數。正因爲如此,編譯器不知道如何處理初始化列表

{{ 0, std::make_shared<Derived>() }} 

它不知道它需要被轉換爲std::initializer_list<std::pair<int, std::shared_ptr<Base>>>重載決議的時間。此外,支撐,初始化列表永遠不會被扣除模板推導出std::initializer_list<T>,所以即使你有這樣的事情

std::make_shared<Derived>({0, 0}) 

Derived有一個合適的構造函數取std::initializer_list<int>,它仍然是行不通的,對同樣的原因:std::make_shared<Derived>將無法​​推斷任何類型的參數。

如何解決這一問題?不幸的是,我看不到任何簡單的方法。但至少現在你應該知道你爲什麼寫了不起作用。

+1

我明白了,謝謝你的回答。 –

相關問題