是不是使用统一初始化危险?

Ete*_*nal 6 uniform-initialization c++11 list-initialization

几天前我发现了统一初始化,我看到每个人都应该尽可能地使用它.

但是,我不禁想到这种新语法比它的价值更麻烦......


第一个例子

假设我写了一个库,其中我有一个像这样的结构:

struct MyStruct
{
    int member0;
    int member1;
}
Run Code Online (Sandbox Code Playgroud)

用户可以使用聚合初始化来编写类似的东西:

MyStruct myVar = {0, 1}; // member0 = 0 and member1 = 1
Run Code Online (Sandbox Code Playgroud)

现在,让我们说我更新了我的库,结构现在看起来像这样:

struct MyStruct
{
    int member0;
    int member1;

    MyStruct(int p0, int p1) : member0(p1), member1(p0){}
}
Run Code Online (Sandbox Code Playgroud)

在C++ 11之前,用户代码将停止编译,这会强制用户重写他的代码并使用构造函数.但是现在,代码将被编译并被解释为统一初始化:

MyStruct myVar = {0, 1}; // member0 = 1 and member1 = 0
Run Code Online (Sandbox Code Playgroud)

没有用户知道,更新他的库将使他的代码做一些非常不同的事情!


第二个例子

现在,让我们说我的库里有这样一个类:

class MyClass
{
public:
    MyClass(int size, int default = 0) : elements(size, default){}
private:
    std::vector<int> elements;
}
Run Code Online (Sandbox Code Playgroud)

用户可以像这样使用它:

MyClass myVar (3,1);  // 3 elements with value 1
Run Code Online (Sandbox Code Playgroud)

或者,使用统一启动,如下所示:

MyClass myVar {3,1};  // 3 elements with value 1
Run Code Online (Sandbox Code Playgroud)

然后,让我们说我更新我的库.该课程现在看起来像这样:

class MyClass
{
public:
    MyClass(int size, int default = 0) : elements(size, default){}
    MyClass(std::initializer_list<int> elts) : elements(elts){}
private:
    std::vector<int> elements;
}
Run Code Online (Sandbox Code Playgroud)

如果使用经典构造函数,则不会出现问题:

MyClass myVar (3,1);  // 3 elements with value 1
Run Code Online (Sandbox Code Playgroud)

但如果调用统一初始化,代码解释将会改变:

MyClass myVar {3,1};  // 2 elements with values 3 and 1
Run Code Online (Sandbox Code Playgroud)

基于这些示例,在我看来,对于用户来说使用统一初始化是非常危险的,因为当将事物添加到使用的库时,代码解释可能会改变,而根本没有任何警告.

更糟糕的是,统一初始化的引入使得聚合初始化变得危险.

我错过了什么吗?是否存在使用统一初始化既安全又有用的上下文?

小智 7

我认为你解决的两个问题与统一初始化本身关系不大,但说明了更改接口的危险.

您可以通过更新库来存档用户代码中相同的次优更改:

struct MyStruct
{
    int member1;
    int member0;
}
Run Code Online (Sandbox Code Playgroud)

没有统一的初始化调用.pre-c ++ 11也可以更改重载决策选择的构造函数:

class some_class
{
    public:
    some_class(int);
}
Run Code Online (Sandbox Code Playgroud)

用户编码器:

some_class var(1.0);
Run Code Online (Sandbox Code Playgroud)

如果代码更改为:

class some_class
{
    public:
    some_class(int);
    some_class(double);
}
Run Code Online (Sandbox Code Playgroud)

将调用第二个构造函数.同样,没有统一的初始化调用,但同样的问题发生.

因此,虽然两个示例都确实展示了可以通过更改库接口来更改用户代码的含义这一事实,但这不是由统一初始化引入或特定的问题,而是次优设计.它只是说明了应该非常仔细地设计库接口的事实.

在概念上,统一初始化提供了一些真正的优势.对于那些人,看到这个优秀的答案