【问题标题】:C++11 universal initialization cause unexpected initialization?C++11 通用初始化导致意外初始化?
【发布时间】:2015-02-08 18:49:18
【问题描述】:

在 C++ 11 中,新的通用初始化语法也可用于调用普通的构造函数(不带 initializer_list 参数)。虽然看起来还不错,但我认为这可能会导致实际使用中出现问题。

所以假设在我的项目中我使用了一个带有以下类的库:

class Foo
{
public:
    Foo(int size, int value); // create 'size' number of elements
    Foo(initializer_list<int> list);  // create elements as in 'list'
}

在项目中是这样使用的:

Foo foo{10, 2};  // initialize foo with 2 elements: 10 and 2

现在库有了一个新版本,在新版本中作者删除了第二个构造函数,该构造函数采用 initializer_list(有意或错误)。我没有注意到变化,我的项目像以前一样愉快地构建,只是初始化了一个意外的 foo(现在它是 10 个元素而不是 2 个)。

这个问题的另一个版本是 Foo 只有第一个构造函数,你使用通用初始化语法来初始化 foo,现在作者决定添加第二个构造函数,这同样导致 foo 用不同的元素初始化没有被注意到。

只是想知道其他人对此的看法。这是一个真正的问题还是我太担心了?有什么解决方案可以防止这种情况发生吗?谢谢。

【问题讨论】:

  • 反对将其视为“通用”并试图普遍使用它当然是一个很好的论据。使用正确的名称“列表初始化”,并且仅在您特别想从列表中初始化时使用它。或者当语法迫使你这样做时,例如类内初始化,或避免令人烦恼的解析。 (但这只是我的意见,不是答案,因为没有真正的答案。)
  • Scott Meyers 在 Effective Modern C++ 的第 7 项中很好地介绍了这一点,是的,这是一个真正的问题。
  • 非常酷的问题。有人可以解释为什么允许跳过括号() 吗?
  • 跟随@ShafikYaghmour:要点是,当使用大括号时,编译器肯定会更喜欢初始化列表构造。事实上,std::vector 有这个确切的问题。当存在这样的构造函数时,必须非常小心地使用 (){}
  • @Angew。我应该更清楚地表述它。为什么决定允许这样做?什么目的?如果第一个必须用foo(10,2) 初始化,第二个用foo({10,2}) 初始化,就不会有问题。

标签: c++ c++11


【解决方案1】:

真正的问题是 API 发生了变化。

如果构造函数是

Foo(int size, int value);

你用过

Foo foo(10, 2);

API 将更改为

Foo(int value, int size);

你会遇到同样的问题。

【讨论】:

  • 我觉得他们不一样。在您的示例中,构造函数已更改其参数 - 显然是不兼容的 API 更改。在我的示例中,任何现有构造函数都不会更改参数和实现。尤其是在类中添加新函数时,人们不会期望 API 以不兼容的方式进行了更改。
  • 在没有初始化器的情况下,同样的方法也可能出现同样的问题,只需将原来的构造函数 Foo(size_t size, int value) 和一个新的构造函数 template &lt;typename... Types&gt; Foo(Types... values)。当您使用Foo foo(10, 2) 调用原始构造函数时,它现在将匹配新构造函数。
  • 确实,模板构造函数也可以达到同样的效果。正如上面 Chiel 和 Aggieboy 所评论的那样,如果 C++ 在这些情况和要求中明确报告歧义,则可以避免该问题。但我想这可能会导致在模板中使用该类时遇到困难。
猜你喜欢
  • 2020-02-08
  • 1970-01-01
  • 1970-01-01
  • 2018-10-11
  • 2012-06-12
  • 2016-04-13
  • 1970-01-01
  • 2021-10-08
  • 2021-09-21
相关资源
最近更新 更多