繁体   English   中英

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

[英]Silent breaking of constructor calls after adding initializer_list constructor

我们考虑以下几点:

#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) ,对于其他构造函数,并且仅当我们真正意味着初始化列表时才使用{}

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

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

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

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

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

一些有用的见解可以在这个答案和评论中找到: https//stackoverflow.com/a/18224556/2968646

没有编译器警告,也永远不会。 警告代码做一些常见的事情是没有意义的

std::vector vec{1};

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM