2011-04-20 103 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 } }); 

(...)现在显然仅仅围绕构造函数指定参数时,并更容易地看到,最{ ... }定义“列表”。

它仍然有点冗长,但它避免了{被用来做两个不同的事情,影响可读性的情况。