对 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
}
前两个案例很明显是在滥用临时对象。根据提问者的评论,此时他们已经了解了 create1
和 create2
中的错误,所以让我们关注 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
中似乎还活着,那就太倒霉了。你的朋友和我的未定义行为是个混蛋。再次.
test
在 Data
之前创建,但也在 Data
之前销毁,使 Data
处于不良状态。
上面的代码都很好地组合在一起 运行 在线 IDE: https://ideone.com/XY30XH
我有一些这样的代码:
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
}
前两个案例很明显是在滥用临时对象。根据提问者的评论,此时他们已经了解了 create1
和 create2
中的错误,所以让我们关注 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
中似乎还活着,那就太倒霉了。你的朋友和我的未定义行为是个混蛋。再次.
test
在 Data
之前创建,但也在 Data
之前销毁,使 Data
处于不良状态。
上面的代码都很好地组合在一起 运行 在线 IDE: https://ideone.com/XY30XH