[英]Silent breaking of constructor calls after adding initializer_list constructor
Let's consider the following: 我们考虑以下几点:
#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
}
Upon running it prints: 在运行它打印:
with int
with int
All good. 都好。 Now due to new requirements I have added a constructor which takes an initializer list.
现在由于新的要求,我添加了一个构造函数,它带有一个初始化列表。
Foo(std::initializer_list<int>) {
std::cout << "with initializer list\n";
}
Now it prints: 现在它打印:
with initializer list
with int
So my old code Foo a{10}
got silently broken. 所以我的旧代码
Foo a{10}
被默默地打破了。 a
was supposed to be initialized with an int
. a
应该用int
初始化。
I understand that the language syntax is considering {10}
as a list with one item. 我理解语言语法正在考虑将
{10}
作为包含一个项目的列表。 But how can I prevent such silent breaking of old code? 但是,如何防止旧代码的这种无声破坏呢?
-Wall -Wextra
. -Wall -Wextra
。 ()
Foo b(20)
, for other constructors and use {}
only when we really meant an initializer list? ()
Foo b(20)
,对于其他构造函数,并且仅当我们真正意味着初始化列表时才使用{}
? It's impossible to generate any warning in these cases, because presented behaviour of choosing std::initializer_list
constructor over direct matches is well defined and compliant with a standard. 在这些情况下不可能生成任何警告,因为在直接匹配中选择
std::initializer_list
构造函数的行为已明确定义并符合标准。
This issue is described in detail in Scott Meyers Effective Modern C++ book Item 7: 这个问题在Scott Meyers Effective Modern C ++ book Item 7中有详细描述:
If, however, one or more constructors declare a parameter of type
std::initializer_list
, calls using the braced initialization syntax strongly prefer the overloads taking std::initializer_lists.但是,如果一个或多个构造函数声明了
std::initializer_list
类型的参数,则使用支撑初始化语法的调用非常喜欢使用std :: initializer_lists的重载。 Strongly.强烈。 If there's any way for compilers to construe a call using a braced initializer to be to a constructor taking a
std::initializer_list
, compilers will employ that interpretation.如果编译器有任何方法将使用支撑初始化程序的调用解释为采用
std::initializer_list
的构造std::initializer_list
,则编译器将采用该解释。
He also presents a few of edge cases of this issue, I strongly recommend reading it. 他还提出了一些关于这个问题的边缘案例,我强烈建议你阅读它。
I couldn't find such an option, so apparently in such cases you should use parentheses for classes that have initializer_list constructor, and uniform initialization for all other classes as you wish. 我找不到这样的选项,所以显然在这种情况下你应该使用括号用于具有initializer_list构造函数的类,并根据需要对所有其他类进行统一初始化。
Some useful insights can be found in this answer and comments to it: https://stackoverflow.com/a/18224556/2968646 一些有用的见解可以在这个答案和评论中找到: https : //stackoverflow.com/a/18224556/2968646
There are no compiler warnings and there never will be. 没有编译器警告,也永远不会。 It just doesn't make sense to warn on code doing something common like
警告代码做一些常见的事情是没有意义的
std::vector vec{1};
Remember that the compiler only warns about really unwanted stuff, like undefined behavior. 请记住,编译器只会警告真正不需要的东西,比如未定义的行为。 It has no way of knowing that in the definition above, you meant to call the constructor taking a size argument.
它无法知道在上面的定义中,你的意思是调用构造函数来获取一个size参数。 For all it knows you actually want to have a vector with one element!
事实上,它知道你实际上想要一个带有一个元素的向量! It can't read your mind :)
它无法读懂你的想法:)
The answer to your second question is basically yes. 你的第二个问题的答案基本上是肯定的。 You can always add a dummy parameter like
struct {} dummy;
您总是可以添加一个虚拟参数,如
struct {} dummy;
to avoid using the constructor with initializer list, but really, the only same solution is just to use parentheses instead of braces (or don't break the interface suddenly). 避免使用带有初始化列表的构造函数,但实际上,唯一相同的解决方案只是使用括号而不是大括号(或者不要突然断开接口)。
If you want to change every portion of code that uses list initialization, you can delete the initializer list constructor, change them to braces, and then implement the constructor correctly. 如果要更改使用列表初始化的代码的每个部分,可以删除初始化列表构造函数,将它们更改为大括号, 然后正确实现构造函数。 I would consider such change a breaking one, and deal with it appropriately.
我会认为这种改变是破坏性的,并适当地处理它。 The other idea would have been to come up with the initializer list use case beforehand, and implement it right away.
另一个想法是事先想出初始化列表用例,并立即实现它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.