invalid_pointer 移动无限制联合的构造函数崩溃
Move constructor of unrestricted union crashes with invalid_pointer
具有不受限制的联合 class 的示例,它包含一个整数映射或一个整数向量:
#include <iostream>
#include <string>
#include <vector>
#include <map>
typedef std::vector<int> int_vec;
typedef std::map<std::string, int> int_map;
struct Vec {
bool is_map;
union {
int_vec int_vec_val;
int_map int_map_val;
};
// MOVE CONSTRUCTORS
Vec(int_vec&& val) : is_map(false), int_vec_val(std::move(val)) {
std::cout << "move vec" << std::endl;
}
Vec(int_map&& val) : is_map(true), int_map_val(std::move(val)) {
std::cout << "move map" << std::endl;
}
Vec(Vec&& rhs) : is_map(rhs.is_map) {
if (is_map) {
int_map_val = std::move(rhs.int_map_val);
} else {
std::cout << "Crashing now ... " << std::endl;
int_vec_val = std::move(rhs.int_vec_val); // <--- how to do this correctly?
}
}
// DESTRUCTOR
~Vec() noexcept {
if (is_map) {
int_map_val.~int_map();
} else {
int_vec_val.~int_vec();
}
}
};
Vec gen_Vec() {
Vec v1(int_vec({1, 2, 3}));
return Vec(std::move(v1));
}
int main() {
Vec v2 = gen_Vec();
}
此代码因 Vec(Vec&&)
移动构造函数中的 *** Error in ./tmp2: munmap_chunk(): invalid pointer: 0x0000000000400f6d ***
而崩溃:
g++ -std=c++11 -O2 tmp2.cpp -lm -o tmp2 && ./tmp2
move vec
Crashing now ...
*** Error in `./tmp2': munmap_chunk(): invalid pointer: 0x0000000000400f6d ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fba03ab47e5]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7fba03ac0ae8]
./tmp2[0x400d0d]
./tmp2[0x400a7c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fba03a5d830]
./tmp2[0x400af9]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:11 13240011 /store/Dropbox/dev/jamr/src/tmp2
00601000-00602000 r--p 00001000 08:11 13240011 /store/Dropbox/dev/jamr/src/tmp2
00602000-00603000 rw-p 00002000 08:11 13240011 /store/Dropbox/dev/jamr/src/tmp2
006ce000-00700000 rw-p 00000000 00:00 0 [heap]
7fba03734000-7fba0383c000 r-xp 00000000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
7fba0383c000-7fba03a3b000 ---p 00108000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
7fba03a3b000-7fba03a3c000 r--p 00107000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
7fba03a3c000-7fba03a3d000 rw-p 00108000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
我的问题是:
移动这些矢量和地图的正确方法是什么?
有没有办法在 mem 初始化列表中完全定义 Vec(Vec&& rhs)
移动构造函数?
非常感谢。
赋值 int_vec_val = std::move(rhs.int_vec_val);
意味着 int_vec_val
表示类型 int_vec
的现有活动对象,并且您想通过分配给它来更改其值。事实并非如此:由于匿名联合体的成员没有默认的成员初始化器,也没有相应的 mem-initializers,因此不会对它们执行任何初始化,所以他们的生命没有开始。
这意味着当进入构造函数块时,int_map_val
和int_vec_val
的none是存活的。您必须使用 placement new:
在正确的位置实际构建正确类型的新对象
Vec(Vec&& rhs) : is_map(rhs.is_map) {
if (is_map) {
std::cout << "move contained map\n";
new(&int_map_val) int_map(std::move(rhs.int_map_val));
} else {
std::cout << "move contained vec\n";
new(&int_vec_val) int_vec(std::move(rhs.int_vec_val));
}
}
你第二个问题的答案是:不,不是真的,因为 rhs.is_map
通常只在 运行 时间和 mem-initializer-list 的内容是已知的 需要在编译时知道。但是,在这种情况下,在构造函数块中进行初始化没有问题(在 ctor-initializer 中进行初始化也没有真正的好处),因为没有对默认为工会成员。
具有不受限制的联合 class 的示例,它包含一个整数映射或一个整数向量:
#include <iostream>
#include <string>
#include <vector>
#include <map>
typedef std::vector<int> int_vec;
typedef std::map<std::string, int> int_map;
struct Vec {
bool is_map;
union {
int_vec int_vec_val;
int_map int_map_val;
};
// MOVE CONSTRUCTORS
Vec(int_vec&& val) : is_map(false), int_vec_val(std::move(val)) {
std::cout << "move vec" << std::endl;
}
Vec(int_map&& val) : is_map(true), int_map_val(std::move(val)) {
std::cout << "move map" << std::endl;
}
Vec(Vec&& rhs) : is_map(rhs.is_map) {
if (is_map) {
int_map_val = std::move(rhs.int_map_val);
} else {
std::cout << "Crashing now ... " << std::endl;
int_vec_val = std::move(rhs.int_vec_val); // <--- how to do this correctly?
}
}
// DESTRUCTOR
~Vec() noexcept {
if (is_map) {
int_map_val.~int_map();
} else {
int_vec_val.~int_vec();
}
}
};
Vec gen_Vec() {
Vec v1(int_vec({1, 2, 3}));
return Vec(std::move(v1));
}
int main() {
Vec v2 = gen_Vec();
}
此代码因 Vec(Vec&&)
移动构造函数中的 *** Error in ./tmp2: munmap_chunk(): invalid pointer: 0x0000000000400f6d ***
而崩溃:
g++ -std=c++11 -O2 tmp2.cpp -lm -o tmp2 && ./tmp2
move vec
Crashing now ...
*** Error in `./tmp2': munmap_chunk(): invalid pointer: 0x0000000000400f6d ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fba03ab47e5]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7fba03ac0ae8]
./tmp2[0x400d0d]
./tmp2[0x400a7c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fba03a5d830]
./tmp2[0x400af9]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:11 13240011 /store/Dropbox/dev/jamr/src/tmp2
00601000-00602000 r--p 00001000 08:11 13240011 /store/Dropbox/dev/jamr/src/tmp2
00602000-00603000 rw-p 00002000 08:11 13240011 /store/Dropbox/dev/jamr/src/tmp2
006ce000-00700000 rw-p 00000000 00:00 0 [heap]
7fba03734000-7fba0383c000 r-xp 00000000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
7fba0383c000-7fba03a3b000 ---p 00108000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
7fba03a3b000-7fba03a3c000 r--p 00107000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
7fba03a3c000-7fba03a3d000 rw-p 00108000 08:01 1065106 /lib/x86_64-linux-gnu/libm-2.23.so
我的问题是:
移动这些矢量和地图的正确方法是什么?
有没有办法在 mem 初始化列表中完全定义
Vec(Vec&& rhs)
移动构造函数?
非常感谢。
赋值 int_vec_val = std::move(rhs.int_vec_val);
意味着 int_vec_val
表示类型 int_vec
的现有活动对象,并且您想通过分配给它来更改其值。事实并非如此:由于匿名联合体的成员没有默认的成员初始化器,也没有相应的 mem-initializers,因此不会对它们执行任何初始化,所以他们的生命没有开始。
这意味着当进入构造函数块时,int_map_val
和int_vec_val
的none是存活的。您必须使用 placement new:
Vec(Vec&& rhs) : is_map(rhs.is_map) {
if (is_map) {
std::cout << "move contained map\n";
new(&int_map_val) int_map(std::move(rhs.int_map_val));
} else {
std::cout << "move contained vec\n";
new(&int_vec_val) int_vec(std::move(rhs.int_vec_val));
}
}
你第二个问题的答案是:不,不是真的,因为 rhs.is_map
通常只在 运行 时间和 mem-initializer-list 的内容是已知的 需要在编译时知道。但是,在这种情况下,在构造函数块中进行初始化没有问题(在 ctor-initializer 中进行初始化也没有真正的好处),因为没有对默认为工会成员。