对 const 引用参数使用默认值会导致崩溃

Using default value for const reference argument causes crash

我有一些这样的代码:

struct Data {
    Data(const std::vector<int> &data = {}) : data_(data) {}
    const std::vector<int> &data_;
};

Data create1() {
    return Data(); // bad
}

Data create2() {
    return Data({}); // bad
}

Data create3(const std::vector<int> &data = {}) {
    return Data(data); // good
}

Data create4() {
    static const std::vector<int> data;
    return Data(data); // good
}

void main() {
    auto data1 = create1(); // deleted data_
    auto data2 = create2(); // deleted data_
    auto data3 = create3(); // ok
    auto data4 = create4(); // ok
}

这四个create函数我觉得是一样的。但是为什么create1和create2导致删除了data_,而create3和create4没问题呢?

创建静态数据成员并将其用于初始化:

struct Data {
    Data(const std::vector<int> &data = empty_) : data_(data) {}
    const std::vector<int> &data_;
private:
    static std::vector<int> empty_;
};

std::vector<int> Data::empty_ = {};

Data create1() {
    return Data(); // good
}

Data create2() {
    return Data({}); // still bad, as it should be
}

Data create3(const std::vector<int> &data = {}) {
    return Data(data); // good
}

Data create4() {
    static const std::vector<int> data;
    return Data(data); // good
}

void main() {
    auto data1 = create1(); // ok
    auto data2 = create2(); // deleted data_
    auto data3 = create3(); // ok
    auto data4 = create4(); // ok
}

前两个案例很明显是在滥用临时对象。根据提问者的评论,此时他们已经了解了 create1create2 中的错误,所以让我们关注 create3 及其工作原理。

剧透:事实并非如此。

我将对代码进行一些修改,以使正在发生的事情更加明显。首先,我们用一个简单的 class 替换 vector,这样我们可以更好地看到构造和破坏。

struct test
{
    test()
    {
        std::cout << "test ctor\n";
    }
    ~test()
    {
        std::cout << "test dtor\n";
    }
};

现在我们做一些类似于 Data 的事情,让它使用 test 而不是 vector

struct Data {
    Data(const test &data = {}) : data_(data) 
    {
        std::cout << "data ctor\n";
    }
    ~Data()
    {
        std::cout << "data dtor\n";
    }
    const test &data_;

    void forceuse() // just to make sure the compiler doesn't get any bright ideas about 
                    // optimizing Data out before we're through with it.
    {
        std::cout << "Using data\n";
    }
};

我们向 create3 添加了一些额外的诊断,并再次将 vector 替换为 test

Data create3(const test &data = {}) {
    std::cout << "in create3\n";
    return Data(data); // good
}

并对main

做同样的事情
int main() {
    { // change the scope of data3 so that we can put a breakpoint at the end of main 
      // and watch all of the fun
        std::cout << "calling create3\n";
        auto data3 = create3(); // ok
        std::cout << "returned from create3\n";
        data3.forceuse();
    }
}

这个输出是

calling create3
test ctor
in create3
data ctor
test dtor
returned from create3
Using data
data dtor

test 在调用 create3 期间创建,并在从 create3 退出时销毁。它在 main 中不存在。如果它在 main 中似乎还活着,那就太倒霉了。你的朋友和我的未定义行为是个混蛋。再次.

testData 之前创建,但也在 Data 之前销毁,使 Data 处于不良状态。

上面的代码都很好地组合在一起 运行 在线 IDE: https://ideone.com/XY30XH