如果copy-list-initialization允许显式构造函数,会出现什么问题?

时间:2012-02-06 07:49:19

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

在C ++标准§13.3.1.7[over.match.list]中,陈述如下:

  

在copy-list-initialization中,如果选择了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

没有问题:调用不明确,代码无法编译,我必须明确选择重载。

f(bar { "answer", 42 }); // ok

由于明确规定了禁令,我觉得我在这里遗漏了一些东西。据我所知,列表初始化选择显式构造函数对我来说似乎不是一个问题:通过使用列表初始化语法,程序员已经表达了进行某种“转换”的愿望。

可能出现什么问题?我错过了什么?

4 个答案:

答案 0 :(得分:25)

概念上,copy-list-initialization是将复合值转换为目标类型。提出措辞和解释理由的论文已经在“复制清单初始化”中考虑了“复制”一词,这是不幸的,因为它并没有真正传达其背后的实际理由。但它保持与现有措辞的兼容性。 {10, 20}对/元组值不应该能够复制初始化String(int size, int reserve),因为字符串不是一对。

考虑显式构造函数但禁止使用。这在以下情况下是有意义的

struct String {
  explicit String(int size);
  String(char const *value);
};

String s = { 0 };

0不传达字符串的值。因此,这会导致错误,因为会考虑两个构造函数,但会选择explicit构造函数,而不是将0视为空指针常量。

不幸的是,这也发生在跨功能的重载解析

void print(String s);
void print(std::vector<int> numbers);

int main() { print({10}); }

由于含糊不清,这也是不正确的。在C ++ 11发布之前,有些人(包括我)认为这是不幸的,但没有提出一篇论文,建议对此进行更改(据我所知)。

答案 1 :(得分:2)

据我所知,关键字显式的目的是拒绝使用此构造函数进行隐式转换。

所以你问为什么显式构造函数不能用于隐式转换?显然是因为该构造函数的作者通过使用关键字显式明确拒绝了它。您发布的标准中的引用仅表明显式关键字也适用于初始化列表(不仅适用于某种类型的简单值)。

添加

更准确地说:与某些构造函数一起使用的关键字显式的目的是让绝对清楚这个构造函数在某个地方使用(即强制所有代码显式调用此构造函数)

f({a,b})是函数的名称时,f之类的IMO语句与显式构造函数调用无关。绝对不清楚(和上下文相关)这里使用哪个构造函数(以及什么类型),例如它取决于存在的函数重载。

另一方面,类似f(SomeType(a,b))的东西是完全不同的东西 - 我们绝对清楚地知道我们使用SomeType类型的构造函数,它接受两个参数a,b并且我们使用函数f重载,最好接受SomeType类型的单个参数。

所以有些构造函数可以隐式使用,如f({a,b}),其他构造函数要求读者使用它们的事实是绝对清楚的,这就是为什么我们声明它们显式

<强> ADD2:

我的观点是:即使没有什么可能出错,有时将构造函数声明为明确是绝对有意义的。建立者是否明确的IMO更多的是它的逻辑而不是任何形式的警告。

E.g。

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

答案 2 :(得分:2)

本声明:

  

在copy-list-initialization中,如果选择了explicit构造函数,则初始化是不正确的。

意味着许多事情。其中,它意味着它必须在显式构造函数中查找。毕竟,如果无法查看它,它就无法选择显式构造函数。当它寻找候选人将支持列表转换成,它必须从所有候选人中选择。即使是以后的人也会被发现是非法的。

如果重载分辨率导致多个功能同样可行,则会导致需要手动用户干预的模糊呼叫。

答案 3 :(得分:1)

是不是因为“显式”是否会停止隐式转换,而你要求它进行隐式转换?

如果您使用单个参数构造函数指定了结构,您会问这个问题吗?