如果复制列表初始化允许显式构造函数,可能会出现什么问题?

2024-04-09

在 C++ 标准 §13.3.1.7 [over.match.list] 中,有以下规定:

在复制列表初始化中,如果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 });

(Note that what happens here is not a conversion, and it would not be one even if the constructor was "implicit". This is initialization of a foo object using its constructor directly. Other than the std::string, there is no conversion here.)

这对我来说似乎完全没问题。隐式转换不可能让我烦恼。

If { "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

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

可能会出什么问题?我缺少什么?


从概念上讲,复制列表初始化是将复合值转换为目标类型。提出措辞并解释基本原理的论文已经认为“复制列表初始化”中的术语“复制”是不幸的,因为它并没有真正传达其背后的实际基本原理。但保留它是为了与现有措辞兼容。 A{10, 20}对/元组值不应该能够复制初始化String(int size, int reserve),因为字符串不是一对。

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

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

String s = { 0 };

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

不幸的是,这也发生在跨函数的重载决策中

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果复制列表初始化允许显式构造函数,可能会出现什么问题? 的相关文章

随机推荐