从初始化列表初始化,但没有 {{{{{{{{ ... }}}}}}}}?
我最近偶然发现了初始化列表的一些问题。考虑一个存储类似地图的数据的程序
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 } } };
我不想这样写。由于我有一个类似映射的类,并且初始化程序具有映射列表的抽象值,因此我想使用以前的版本,并且独立于任何此类实现细节,例如构造函数的嵌套级别。
一种解决方法是声明一个初始值设定项列表构造函数
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;
};
现在我可以使用前者,因为当我有一个初始值设定项列表构造函数时,整个初始值设定项列表将被视为一个元素,而不是被拆分为多个元素。但我认为构造函数的这种单独需求非常丑陋。
我正在寻求指导:
- 您对前一种和后一种初始化形式有何看法?在这种情况下,需要额外的牙套有意义吗?
- 您认为在这种情况下添加初始值设定项列表构造函数的要求不好吗?
如果您同意我的观点,即前一种初始化方式更好,您能想到什么解决方案?
I recently stumbles across some problem with initializer lists. Consider a program that stores map-like data
struct MyMapLike {
MyMapLike(std::map<std::string, int> data)
:data(std::move(data))
{ }
private:
std::map<std::string, int> data;
};
That looks straight forward. But when initializing it, it becomes ugly. I want to let it look like
MyMapLike maps = { { "One", 1 }, { "Two", 2 } };
But the compiler doesn't want to accept this, because the above means that it should look for a two-parameter constructor that can accept { "One", 1 }
and { "Two", 2 }
respectively. I need to add extra braces, to make it look like a single-parameter constructor accepting the { { ... }, { ... } }
MyMapLike maps = { { { "One", 1 }, { "Two", 2 } } };
I would not like to write it like that. Since I have a map-like class, and the initializer has the abstract value of a mapping-list, I would like to use the former version, and be independent of any such implementation details like level of nesting of constructors.
One work around is to declare an initializer-list constructor
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;
};
Now I can use the former, because when I have an initializer-list constructor, the whole initializer list is treated as one element instead of being splitted into elements. But I think this separate need of the constructor is dead ugly.
I'm looking for guidance:
- What do you think about the former and latter form of initialization? Does it make sense to be required to have extra braces in this case?
- Do you consider the requirement for addition of an initializer list constructor in this case bad?
If you agree with me on that the former way of initialization is nicer, what solutions can you think of?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
,这就是问题所在:由你提供允许将您的类视为地图的构造函数。您称您的解决方案为解决方法,但没有什么可以解决的。 :)
确实如此,但不幸的是,由于它是您的类,因此您必须指定初始值设定项列表的工作方式。
And herin lies the problem: it's up to you to supply the constructors that allow your class to be treated like a map. You called your solution a work-around, but there's nothing to work around. :)
It is, but unfortunately since it's your class, you have to specify how the initializer lists work.
我想是的。我认为,不仅当语法与构造函数匹配时,而且当语法与构造函数的单个参数的某些构造函数匹配时,都允许调用构造函数,等等,这会导致太多的歧义。
不会。它可以让您获得所需的语法,但不会产生如果我们使编译器更难搜索构造函数而出现的问题。
I think so. I think it would permit too much ambiguity to allow a constructor be called not just when the syntax matches that constructor, but when the syntax matches some constructor of the single argument to the constructor, and so on recursively.
No. It lets you get the syntax you want but without creating the problems that arise if we make the compiler search harder for a constructor to use.
这样的东西不会给出所需的效果(
MyMapLike
可以以std::map
可以的任何方式构造,但不会隐式转换为std: :地图
)?如果它绝对必须是成员,也许可以使用构造函数完美转发(我不确定确切的语法),如下所示:
Wouldn't something like this give the desired effect (
MyMapLike
can be constructed in any way thatstd::map
can, but does not implicitly convert tostd::map
)?If it absolutely positively has to be a member, maybe use constructor perfect forwarding (I'm not sure about the exact syntax) along the lines of:
您可以按如下方式初始化:
外部
( ... )
现在显然只是简单地包围构造函数参数,并且更容易看到最外部的{ ... }
定义“列表”。它仍然有点冗长,但它避免了
{
被用来做两件不同事情的情况,从而影响可读性。You could initialize as follows:
The outer
( ... )
are now clearly simply to surround the constructor args, and it's easier to see that the outermost{ ... }
defines the 'list'.It's still a bit verbose, but it avoids the situation where
{
is being used to do two different things, which affects readability.我同意这很丑。一旦我超出了非常简单的情况,我通常会废弃初始化列表,因为它们非常有限。对于你的情况,我会选择 boost::assign ,虽然不那么简洁,但可以提供更好的灵活性和控制。
I agree that it's ugly. I usually scrap initializer lists once I move beyond very simple cases as they're quite limited. For your case I'd go with boost::assign, which while not as terse gives better flexibility and control.
我更喜欢第一种形式,因为它有一个干净的类接口。采用初始值设定项列表的构造函数会污染接口并且提供很少的回报。
额外的大括号是我们会习惯的。就像我们习惯了 C++ 的其他怪癖一样。
I prefer the first form because it has a clean class interface. The constructor taking an initializer list pollutes the interface and offers little in return.
The extra braces are something we'll get used to. Just like we got used to C++'s other quirks.