2011-04-20 162 views
24

我最近在初始化列表中遇到了一些問題。考慮一個存儲地圖狀數據的程序從初始化程序列表初始化,但沒有{{{{{{{{...}}}}}}}}?

struct MyMapLike { 
    MyMapLike(std::map<std::string, int> data) 
    :data(std::move(data)) 
    { } 

private: 
    std::map<std::string, int> data; 
}; 

這看起來很直接。但是初始化時,它變得很難看。我想讓它看起來像

MyMapLike maps = { { "One", 1 }, { "Two", 2 } }; 

但是,編譯器並不想接受這一點,因爲上面意味着它應該尋找一個兩個參數的構造函數,可以分別接受{ "One", 1 }{ "Two", 2 }。我需要添加額外的支架,使它看起來像一個單一參數的構造函數接受{ { ... }, { ... } }

MyMapLike maps = { { { "One", 1 }, { "Two", 2 } } }; 

我不希望把它寫這樣的。因爲我有一個類似map的類,並且初始化程序具有映射列表的抽象值,所以我想使用前一個版本,並且獨立於任何這樣的實現細節,比如構造函數的嵌套層次。

一個解決辦法是宣佈一個初始化列表構造

struct MyMapLike { 
    MyMapLike(std::initializer_list< 
    std::map<std::string, int>::value_type 
    > vals) 
    :data(vals.begin(), vals.end()) 
    { } 

    MyMapLike(std::map<std::string, int> data) 
    :data(std::move(data)) 
    { } 

private: 
    std::map<std::string, int> data; 
}; 

現在我可以用前者,因爲當我有一個初始化列表構造,整個初始化列表作爲一個元素,而不是治療被分解成元素。但我認爲構造函數的這個單獨的需求是醜陋的。

我在尋找指導:

  • 你認爲怎麼樣初始化前者和後者的形式?在這種情況下需要額外的支架是否有意義?
  • 您是否認爲在這種情況下添加初始化程序列表構造函數的要求很差?

如果你同意我的看法,以前的初始化方式更好,你會想到什麼解決方案?

+0

@MooingDuck,我想他就是這麼做的!請參閱':: value_type'。 – 2013-12-21 20:11:20

+0

「額外」的外部'{}'做與內部不同的東西:它們是大括號初始化語法的一部分,表明構造函數調用正在發生,但是* not *是實際對象的一部分被傳遞給構造函數。同時,內括號表示映射的初始化程序列表的實際開始。我認爲這很有道理,事實上,Clang警告最外面的一系列大括號中的某些(合法的)部分,所以我期望'{{/ * ... stuff ... * /}}'將會變得相當標準他們更喜歡使用大括號初始化語法。 – 2015-12-15 21:34:49

回答

0

我同意這很醜。一旦我移出非常簡單的案例,因爲它們非常有限,我通常會刪除初始化器列表。對於你的情況,我會使用boost :: assign,儘管這不是簡單的提供了更好的靈活性和控制。

4

會不會給出所需的效果(MyMapLike可以以任何方式構建,但不會隱式轉換爲std::map)?

struct MyMapLike : private std::map<std::string, int> 
{ 
    using map::map; 
}; 

如果絕對肯定必須是會員,也許還可以利用構造完美轉發(我不知道確切的語法)的線沿線的:

struct MyMapLike 
{ 
    template<typename... Initializers> 
    MyMapLike(Initializers... init, decltype(new std::map<std::string, int>(...init)) = 0) 
     : data(...init) 
    { } 

private: 
    std::map<std::string, int> data; 
}; 
+0

是的,但繼承不是一種選擇。例如,我認爲有一個'variant'>'可以同時包含'map'和'int'。我的問題中的代碼是一個例子,這不是我想要解決的最終問題。 – 2011-04-20 21:16:01

+0

好吧,一組模板參數包轉發構造函數,爲你的聯合中的每個成員使用一個模板參數包轉發構造函數,並使用'enable_if'來確保適當的(並且只有一個)被啓用?我不知道確切的語法,但我會嘗試... – 2011-04-20 21:41:08

+0

更新此答案會很好,因爲語法更爲人知。 – bames53 2012-04-02 19:54:37

10

因爲我有一個地圖狀類,並初始化具有映射列表的抽象價值,我想用以前版本

而且herin就是問題所在:它是由提供的構造函數允許你的類被視爲一個地圖。你稱你的解決方案爲解決方法,但沒有什麼可以解決的。 :)

但我認爲這個單獨的構造函數的需求是死的醜陋。

這是,但不幸的是,因爲它是你的班級,你必須指定初始值設定項列表的工作方式。

+3

雖然有時候我不知道。當我有一個具有'T'的構造函數的泛型類時,我不知道如何爲它編寫初始化程序列表構造函數:( – 2011-04-21 17:08:15

+0

@Johannes:Hm。:( – GManNickG 2011-04-22 02:14:36

+0

@ JohannesSchaub-litb我不確定「T」的構造函數意味着什麼 - 你的意思是類似於'template Foo :: Foo(const T&)'?或者類似於'template template Foo :: Foo(T2 &&)'? – 2015-12-15 21:37:25

0

您如何看待前者和後者的初始化形式?在這種情況下需要額外的支架是否有意義?

我更喜歡第一種形式,因爲它有一個乾淨的類接口。採用初始化程序列表的構造函數會污染接口,並且返回的結果很少。

額外的大括號是我們會習慣的。就像我們習慣了C++的其他怪癖一樣。

+0

當你得到正確的initializer_list時,我不認爲需要額外的大括號。 – 2012-04-29 18:31:05

6

您如何看待前者和後者的初始化形式?在這種情況下需要額外的支架是否有意義?

我這麼認爲。我認爲它會允許太多的含糊不清,以便不僅在語法與構造函數匹配時調用構造函數,而且在語法與構造函數的單個參數的某些構造函數匹配時等等。

struct A { int i; }; 
struct B { B(A) {} B(int) {} }; 
struct C { C(B) {} }; 

C c{1}; 

你考慮另外在這種情況下的初始化列表構造不良的要求?

不需要。它可以讓您獲得所需的語法,但不會產生如果編譯器搜索難以使用構造函數時出現的問題。

1

你可以初始化如下:

MyMapLike maps ({ { "One", 1 }, { "Two", 2 } }); 

(...)現在顯然僅僅圍繞構造函數指定參數時,並更容易地看到,最{ ... }定義「列表」。

它仍然有點冗長,但它避免了{被用來做兩個不同的事情,影響可讀性的情況。