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

tas*_*oor 20 c++ initializer-list c++11

我们考虑以下几点:

#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
}
Run Code Online (Sandbox Code Playgroud)

在运行它打印:

with int
with int
Run Code Online (Sandbox Code Playgroud)

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

Foo(std::initializer_list<int>) {
    std::cout << "with initializer list\n";
}
Run Code Online (Sandbox Code Playgroud)

现在它打印:

with initializer list
with int
Run Code Online (Sandbox Code Playgroud)

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

我理解语言语法正在考虑{10}作为包含一个项目的列表.但是,如何防止旧代码的这种无声破坏呢?

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

Out*_*ned 5

在这些情况下不可能生成任何警告,因为选择std::initializer_list构造函数而非直接匹配的行为已明确定义并符合标准.

这个问题在Scott Meyers Effective Modern C++ book Item 7中有详细描述:

但是,如果一个或多个构造函数声明了一个类型的参数 std::initializer_list,则使用支撑初始化语法的调用非常喜欢使用std :: initializer_lists的重载.强烈.如果编译器有任何方法将使用支撑初始化器的调用解释为构造函数采用a std::initializer_list,编译器将使用该解释.

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