使用Invalid_pointer移动无限制联合崩溃的构造函数

Move constructor of unrestricted union crashes with invalid_pointer

本文关键字:崩溃 构造函数 无限制 Invalid pointer 移动 使用      更新时间:2023-10-16

一个示例的示例是无限制的联合类,该类别具有ints的地图或ints的向量:

#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

我的问题是:

  1. 移动这些向量和地图的正确方法是什么?

  2. 是否有一种方法可以完全定义Vec(Vec&& rhs)在MEM初始器列表中移动构造函数?

非常感谢。

分配int_vec_val = std::move(rhs.int_vec_val);表示int_vec_val表示类型int_vec的现有的,实时对象,您想通过分配给它来更改其值。事实并非't开始。

这意味着当输入构造函数的块时,int_map_valint_vec_val都不活着。您必须使用新的位置在正确的位置实际构建正确的位置的新对象:

Vec(Vec&& rhs) : is_map(rhs.is_map) {
   if (is_map) {
      std::cout << "move contained mapn";
      new(&int_map_val) int_map(std::move(rhs.int_map_val));
   } else {
      std::cout << "move contained vecn";
      new(&int_vec_val) int_vec(std::move(rhs.int_vec_val));
   }
}

您第二个问题的答案是:不,不是真的,因为rhs.is_map通常仅在运行时知道,并且需要在编译时知道 mem-Initializer-list-list 的内容。但是,在这种情况下,在构造函数的块中进行初始化没有问题(并且在 ctor-initializer 中没有获得真正的好处),因为没有对工会成员执行初始化默认情况下。