添加initializer_list构造函数后,静默断开构造函数调用

时间:2017-09-25 05:53:03

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

让我们考虑以下几点:

#include <iostream>
#include <initializer_list>

class Foo {
public:
    Foo(int) {
        std::cout << "with int\n";
    }
};

int main() {
    Foo a{10};  // new style initialization
    Foo b(20);  // old style initialization
}

运行时打印:

with int
with int

一切都好。现在由于新的要求,我添加了一个带有初始化列表的构造函数。

Foo(std::initializer_list<int>) {
    std::cout << "with initializer list\n";
}

现在打印:

with initializer list
with int

所以我的旧代码Foo a{10}被默默地打破了。 a应该使用int进行初始化。

我理解语言语法正在考虑将{10}作为包含一个项目的列表。但是,我怎样才能防止旧代码的这种无声破坏?

  1. 是否有任何编译器选项会对此类情况发出警告?由于这将是编译器特定的,我最感兴趣的是gcc。我已经尝试过-Wall -Wextra
  2. 如果没有这样的选项那么我们总是需要使用旧样式构造,即使用() Foo b(20),用于其他构造函数,并且仅当我们真正意味着初始化程序时才使用{}列表?

3 个答案:

答案 0 :(得分:5)

在这些情况下无法生成任何警告,因为在直接匹配中选择std::initializer_list构造函数的行为已明确定义且符合标准。

Scott Meyers Effective Modern C++ book中详细介绍了此问题  第7项:

  

但是,如果一个或多个构造函数声明了一个类型的参数   std::initializer_list,使用支持的初始化语法进行调用   非常喜欢使用std :: initializer_lists的重载。强烈。   如果编译器有任何方法可以使用支撑来解释一个呼叫   初始化器是一个构造函数,取std::initializer_list,   编制者将采用这种解释。

他还提出了一些关于这个问题的边缘案例,我强烈建议你阅读它。

答案 1 :(得分:3)

我找不到这样的选项,所以显然在这种情况下你应该对具有initializer_list构造函数的类使用括号,并根据需要对所有其他类进行统一初始化。

可以在此答案中找到一些有用的见解并对其进行评论:https://stackoverflow.com/a/18224556/2968646

答案 2 :(得分:2)

没有编译器警告,也永远不会。警告代码执行像

这样的常见事情是没有意义的
std::vector vec{1};

请记住,编译器只会警告真正不需要的东西,比如未定义的行为。它无法知道在上面的定义中,你的意思是调用构造函数来获取一个size参数。事实上,它知道你实际上想要拥有一个元素的向量!它无法读懂你的想法:)

你的第二个问题的答案基本上是肯定的。你总是可以添加一个虚拟参数,如struct {} dummy;,以避免使用带有初始化列表的构造函数,但实际上,唯一相同的解决方案只是使用括号而不是括号(或者不要突然破坏界面)。

如果要更改使用列表初始化的代码的每个部分,可以删除初始化列表构造函数,将其更改为大括号,然后然后正确实现构造函数。我会认为这种改变是破坏性的,并适当地处理它。另一个想法是事先提出初始化列表用例,并立即实现它。