2016-12-25 11 views
2

Примером с неограниченным классом союза, который содержит либо карту Интса или вектор целых чисел:Переместить конструктор неограниченных союза аварий с invalid_pointer

#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(); 
} 

Этого код происходит сбой с *** Error in ./tmp2: munmap_chunk(): invalid pointer: 0x0000000000400f6d *** в конструкторе Vec(Vec&&) двигаться:

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?

Большое спасибо.

+0

Вам необходимо разместить новое. –

+0

@ T.C Я использую 'new (& int_vec_val) int_vec (rhs.int_vec_val);' в моем конструкторе копирования, но это влечет за собой копирование вектора AFAICU. Или вы имеете в виду что-то еще? – VitoshKa

ответ

2

Назначение int_vec_val = std::move(rhs.int_vec_val); подразумевает, что int_vec_val обозначает существующий, живой объект типа int_vec, и вы хотите изменить его значение, назначив ему. Это не тот случай: поскольку члены анонимного объединения не имеют инициализаторов элемента по умолчанию и не имеют соответствующего mem-initializer, либо их инициализация не выполняется, поэтому их срок службы не начинается.

Это означает, что ни один из int_map_val и int_vec_val не активен, когда введен блок конструктора. Вы должны фактически построить новый объект нужного типа в нужном месте с помощью размещения нового:

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), поскольку по умолчанию члены профсоюза не выполняются.

+1

В качестве примечания, 'Vec (Vec const &)' может быть реализовано через конструкторы пересылки, целиком в списке инициализаторов, после того, как у вас есть 3, которые OP написал просто 'Vec (Vec const & rhs): Vec (rhs.is_map? Vec (rhs.int_map_val): Vec (rhs.int_vec_val)) и 'Vec (int_vec const & Val): Vec (int_vec (Val)) {}' и т. д. – Yakk

+0

@Yakk Хорошо, это помогает избежать дублирования кода. Хорошая реализация должна генерировать только одну дополнительную конструкцию движения для 'int_vec' или' int_val', что немаловажно. И даже этого можно избежать, дублируя только реализацию конструкторов из 'const int_vec &' и 'const int_map &'. Невозможность написать весь конструктор копий 'Vec' по-прежнему является чистым выигрышем. – bogdan

 Смежные вопросы

  • Нет связанных вопросов^_^