我最近遇到了初始化列表的一些问题。考虑一个存储类似地图数据的程序
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 :(得分: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 ++的其他怪癖一样。