从初始化程序列表初始化,但没有{{{{{{{{...}}}}}}}}?
我最近在初始化列表中遇到了一些问题。考虑一个存储地图状数据的程序从初始化程序列表初始化,但没有{{{{{{{{...}}}}}}}}?
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;
};
现在我可以用前者,因为当我有一个初始化列表构造,整个初始化列表作为一个元素,而不是治疗被分解成元素。但我认为构造函数的这个单独的需求是丑陋的。
我在寻找指导:
- 你认为怎么样初始化前者和后者的形式?在这种情况下需要额外的支架是否有意义?
- 您是否认为在这种情况下添加初始化程序列表构造函数的要求很差?
如果你同意我的看法,以前的初始化方式更好,你会想到什么解决方案?
我同意这很丑。一旦我移出非常简单的案例,因为它们非常有限,我通常会删除初始化器列表。对于你的情况,我会使用boost :: assign,尽管这不是简单的提供了更好的灵活性和控制。
会不会给出所需的效果(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;
};
是的,但继承不是一种选择。例如,我认为有一个'variant'>'可以同时包含'map'和'int'。我的问题中的代码是一个例子,这不是我想要解决的最终问题。 – 2011-04-20 21:16:01
好吧,一组模板参数包转发构造函数,为你的联合中的每个成员使用一个模板参数包转发构造函数,并使用'enable_if'来确保适当的(并且只有一个)被启用?我不知道确切的语法,但我会尝试... – 2011-04-20 21:41:08
更新此答案会很好,因为语法更为人知。 – bames53 2012-04-02 19:54:37
因为我有一个地图状类,并初始化具有映射列表的抽象价值,我想用以前版本
而且herin就是问题所在:它是由你提供的构造函数允许你的类被视为一个地图。你称你的解决方案为解决方法,但没有什么可以解决的。 :)
但我认为这个单独的构造函数的需求是死的丑陋。
这是,但不幸的是,因为它是你的班级,你必须指定初始值设定项列表的工作方式。
虽然有时候我不知道。当我有一个具有'T'的构造函数的泛型类时,我不知道如何为它编写初始化程序列表构造函数:( – 2011-04-21 17:08:15
@Johannes:Hm。:( – GManNickG 2011-04-22 02:14:36
@ JohannesSchaub-litb我不确定“T”的构造函数意味着什么 - 你的意思是类似于'template
您如何看待前者和后者的初始化形式?在这种情况下需要额外的支架是否有意义?
我更喜欢第一种形式,因为它有一个干净的类接口。采用初始化程序列表的构造函数会污染接口,并且返回的结果很少。
额外的大括号是我们会习惯的。就像我们习惯了C++的其他怪癖一样。
当你得到正确的initializer_list时,我不认为需要额外的大括号。 – 2012-04-29 18:31:05
您如何看待前者和后者的初始化形式?在这种情况下需要额外的支架是否有意义?
我这么认为。我认为它会允许太多的含糊不清,以便不仅在语法与构造函数匹配时调用构造函数,而且在语法与构造函数的单个参数的某些构造函数匹配时等等。
struct A { int i; };
struct B { B(A) {} B(int) {} };
struct C { C(B) {} };
C c{1};
你考虑另外在这种情况下的初始化列表构造不良的要求?
不需要。它可以让您获得所需的语法,但不会产生如果编译器搜索难以使用构造函数时出现的问题。
你可以初始化如下:
MyMapLike maps ({ { "One", 1 }, { "Two", 2 } });
外(...)
现在显然仅仅围绕构造函数指定参数时,并更容易地看到,最{ ... }
定义“列表”。
它仍然有点冗长,但它避免了{
被用来做两个不同的事情,影响可读性的情况。
@MooingDuck,我想他就是这么做的!请参阅':: value_type'。 – 2013-12-21 20:11:20
“额外”的外部'{}'做与内部不同的东西:它们是大括号初始化语法的一部分,表明构造函数调用正在发生,但是* not *是实际对象的一部分被传递给构造函数。同时,内括号表示映射的初始化程序列表的实际开始。我认为这很有道理,事实上,Clang警告最外面的一系列大括号中的某些(合法的)部分,所以我期望'{{/ * ... stuff ... * /}}'将会变得相当标准他们更喜欢使用大括号初始化语法。 – 2015-12-15 21:34:49