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

时间:2014-05-04 11:58:41

标签: c++11 uniform-initialization list-initialization

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

但是,我无法帮助您认为这种新语法比它的价值更麻烦......


第一个例子

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

struct MyStruct
{
    int member0;
    int member1;
}

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

MyStruct myVar = {0, 1}; // member0 = 0 and member1 = 1

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

struct MyStruct
{
    int member0;
    int member1;

    MyStruct(int p0, int p1) : member0(p1), member1(p0){}
}

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

MyStruct myVar = {0, 1}; // member0 = 1 and member1 = 0

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


第二个例子

现在,让我们说我的图书馆里有这样的课程:

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

用户可以像这样使用它:

MyClass myVar (3,1);  // 3 elements with value 1

或使用统一启动,如下:

MyClass myVar {3,1};  // 3 elements with value 1

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

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;
}

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

MyClass myVar (3,1);  // 3 elements with value 1

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

MyClass myVar {3,1};  // 2 elements with values 3 and 1

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

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

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

1 个答案:

答案 0 :(得分:7)

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

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

struct MyStruct
{
    int member1;
    int member0;
}

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

class some_class
{
    public:
    some_class(int);
}

用户编码器:

some_class var(1.0);

如果代码更改为:

class some_class
{
    public:
    some_class(int);
    some_class(double);
}

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

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

在概念上,统一初始化提供了一些真正的优势。对于那些,请参阅此excellent answer