如果copy-list-initialization允许显式的构造函数,会出现什么问题?
在C ++标准§13.3.1.7[over.match.list]中,陈述如下:
在复制列表初始化中,如果select
explicit
构造函数,则初始化是不合格的。
这就是我们无法做到的原因,例如:
struct foo { // explicit because it can be called with one argument explicit foo(std::string s, int x = 0); private: // ... }; void f(foo x); f({ "answer", 42 });
(请注意,这里发生的不是一个转换 ,即使构造函数是“隐式的”也不会是一个,这是直接使用构造函数初始化一个foo
对象,除了std::string
,没有转换这里。)
这对我来说似乎很好。 没有办法,隐式转换会咬我。
如果{ "answer", 42 }
可以初始化别的东西,编译器就不会背叛我,做错了事情:
struct bar { // explicit because it can be called with one argument explicit bar(std::string s, int x = 0); private: // ... }; void f(foo x); void f(bar x); f({ "answer", 42 }); // error: ambiguous call
没有问题:调用不明确,代码不能编译,我必须明确地select超载。
f(bar { "answer", 42 }); // ok
由于禁止明确规定,我有这种感觉,我在这里失去了一些东西。 就我所见,列表初始化select显式构造函数对我来说似乎不是一个问题:通过使用列表初始化语法,程序员已经expression了做某种“转换”的愿望。
有什么可能出错? 我错过了什么?
从概念上来说,复制列表初始化是将复合值转换为目标types。 提出措辞和解释理由的论文在“副本清单初始化”中已经考虑到“复制”一词,因为它并没有真正传达它背后的真正原理。 但是为了与现有的措词兼容而保存。 {10, 20}
pair / tuple值不应该能够复制初始化一个String(int size, int reserve)
,因为一个string不是一对。
显式的构造函数被认为是被禁止使用的。 这在以下情况下是有意义的
struct String { explicit String(int size); String(char const *value); }; String s = { 0 };
0
不传达一个string的值。 所以这会导致一个错误,因为这两个构造函数都被考虑过了,但是一个explicit
构造函数被选中,而不是0
被视为一个空指针常量。
不幸的是,这也发生在跨function的重载parsing中
void print(String s); void print(std::vector<int> numbers); int main() { print({10}); }
这也是因为含糊不清而形成的。 C ++ 11之前的一些人(包括我)被释放后认为这是不幸的,但是没有提出一个提出改变的文章(据我所知)。
这个说法:
在复制列表初始化中,如果select
explicit
构造函数,则初始化是不合格的。
意味着很多事情。 其中,这意味着它必须看明确的构造函数。 毕竟,如果看不见它,它就不能select一个显式的构造函数。 当它寻找候选人转换支撑列表时,它必须从所有候选人中select。 即使那些后来被发现是非法的。
如果重载分辨率导致多个function同样可行,则会导致需要手动用户干预的模糊呼叫。
据我所知明确的关键字的目的是拒绝隐式转换与此构造函数。
所以你问为什么显式构造函数不能用于隐式转换? 很明显,因为该构造函数的作者明确地否定了关键字explicit 。 你所发布的标准中的引用只是声明显式关键字也适用于初始化列表(而不仅仅是某种types的简单值)。
加:
要更正确地说:与某个构造函数一起使用的关键字显式的目的是使得这个构造函数在某个地方被使用(即迫使所有的代码明确地调用这个构造函数)。
而像f({a,b})
f
是f
函数名的IMO语句与显式的构造函数调用没有任何关系。 这是绝对不清楚(和上下文相关的)在这里使用了哪个构造函数(以及什么types),例如它取决于函数的重载。
另一方面,像f(SomeType(a,b))
是完全不同的东西 – 这是非常清楚的,我们使用SomeType
types的构造函数接受两个参数a,b
,我们使用函数f
overload最好接受SomeType
types的单参数。
所以一些构造函数可以像f({a,b})
一样隐式使用f({a,b})
而另一些构造函数要求读者使用它的事实是完全清楚的,这就是为什么我们声明它们是明确的 。
ADD2:
我的观点是:有时,即使没有任何事情可能出错,声明构造函数也是有意义的。 海事组织是否构造是明确的更多的是它的逻辑问题,而不是任何forms的警告。
例如
double x = 2; // looks absolutely natural std::complex<double> x1 = 3; // also looks absolutely natural std::complex<double> x2 = { 5, 1 }; // also looks absolutely natural
但
std::vector< std::set<std::string> > seq1 = 7; // looks like nonsense std::string str = some_allocator; // also looks stupid
是不是因为“显式”在那里停止隐式转换,而是要求它做一个隐式转换?
你会问,如果你已经用一个参数构造函数指定了结构?