从初始化列表初始化,但没有{{{{{{{{{}}}}}}}}?

时间:2011-04-20 16:50:13

标签: c++ c++11 initializer-list

我最近遇到了初始化列表的一些问题。考虑一个存储类似地图数据的程序

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;
};

现在我可以使用前者,因为当我有一个初始化列表构造函数时,整个初始化列表被视为一个元素而不是被分割成元素。但我认为构造函数的这种单独需求是死的。

我正在寻找指导:

  • 您如何看待前者和后者的初始化形式?在这种情况下需要额外的括号是否有意义?
  • 您是否认为在这种情况下添加初始化列表构造函数的要求不好?

如果您同意我之前的初始化方式更好,您能想到什么解决方案?

6 个答案:

答案 0 :(得分:10)

  

由于我有类似地图的类,并且初始化程序具有映射列表的抽象值,我想使用以前的版本

而herin就是问题所在:由来提供允许将你的类视为地图的构造函数。你把你的解决方案称为解决方案,但是没有什么可以解决的。 :)

  

但是我认为构造函数的这种单独需求是丑陋的。

确实如此,但不幸的是,因为它是你的课程,你必须指定初始化程序列表的工作方式。

答案 1 :(得分:6)

  

您如何看待前者和后者的初始化形式?在这种情况下需要额外支撑是否有意义?

我是这么认为的。我认为允许构造函数不仅在语法与构造函数匹配时调用构造函数,而且在语法与构造函数的单个参数的某个构造函数相匹配时,依此类推,这样会产生过多的歧义。

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

C c{1};
  

在这种情况下,您是否认为添加初始化列表构造函数的要求不好?

没有。它可以让你获得你想要的语法,但是如果我们让编译器更难以使用构造函数,那么就不会产生问题。

答案 2 :(得分:4)

这样的事情会不会产生预期的效果(MyMapLike可以用std::map可以构建的任何方式构建,但不会隐式转换为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;
};

答案 3 :(得分:1)

您可以按如下方式初始化:

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

外部( ... )现在显然只是围绕构造函数args,并且更容易看到最外面的{ ... }定义了“列表”。

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

答案 4 :(得分:0)

我同意这很难看。一旦我超越非常简单的情况,我通常会废弃初始化列表,因为它们非常有限。对于你的情况,我会选择boost :: assign,虽然不像terse那样提供更好的灵活性和控制。

答案 5 :(得分:0)

  

您如何看待前者和后者的初始化形式?在这种情况下需要额外支撑是否有意义?

我更喜欢第一种形式,因为它有一个干净的类接口。采用初始化列表的构造函数会污染接口并提供很少的回报。

额外的牙套是我们习惯的东西。就像我们习惯了C ++的其他怪癖一样。