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

时间:2018-07-05 23:05:25

标签: c++ reference default-arguments

我有一些这样的代码:

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
}

这四个创建函数对我来说似乎是相同的。但是为什么create1和create2导致删除的data_但create3和create4没问题?

2 个答案:

答案 0 :(得分:1)

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

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
}

答案 1 :(得分:1)

前两个案例非常明显地滥用了临时人员。根据提问者的评论,他们已经了解到create1create2出了什么问题,所以让我们集中讨论create3及其起作用的原因。

剧透:没有。

我将对代码进行一些自由处理,以使发生的事情更加明显。首先,我们用一个简单的类替换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