Вызывается деструктор члена Союза

С++ 11 допускает использование стандартных типов макета в union: Член союза имеет пользовательский конструктор

Теперь мой вопрос: Я уверен, что пользовательский деструктор будет вызван, когда union выходит за рамки?

Я понимаю, что мы должны вручную уничтожать и строить при переключении: http://en.cppreference.com/w/cpp/language/union#Explanation

Но как насчет примера вроде этого:

{
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

Когда s выходит за пределы области видимости, я пропустил в памяти строку, выделенную в куче, потому что я не вызывал string деструктор?

Ответы

Ответ 1

В вашем примере, который вы предоставили str, не будет разрушен. Стандартные состояния в [class.union]/2

Объединение может иметь функции-члены (включая конструкторы и деструкторы), но не виртуальные (10.3) функции. Союз не должен иметь базовые классы. Объединение не должно использоваться в качестве базового класса. Если объединение содержит нестатический элемент данных ссылочного типа, программа плохо сформирована. Не более чем один нестатический член данных объединения может иметь бит-или-равный-инициализатор. [ Примечание: если какой-либо нестатический член данных объединения имеет нетривиальный конструктор по умолчанию (12.1), конструктор копирования (12.8), конструктор перемещения (12.8), оператор назначения копирования (12.8), оператор присваивания перемещения ( 12.8) или деструктор (12.4), соответствующая членская функция объединения должна быть предоставлена ​​пользователем или она будет неявно удалена (8.4.3) для объединения. - end note]

акцент мой

Так как оба str и vec имеют специальные функции-члены, которые не являются тривиальными, вам нужно будет предоставить их для объединения самостоятельно.

Заметьте, что в соответствии с комментариями bogdan ниже пустого деструктора недостаточно. В [class.union]/8 имеем

[...] Если X является объединением, его вариантными членами являются нестатические элементы данных, [...]

Таким образом, все члены этого союза являются вариантами. Тогда, если мы посмотрим на [class.dtor]/8, имеем

После выполнения тела деструктора и уничтожения любых автоматических объектов, выделенных в теле, деструктор класса X вызывает деструкторы для Xs прямых невариантных нестатических членов данных [...]

Таким образом, деструктор не будет автоматически уничтожать членов объединения, поскольку они являются вариантами.

Вы можете сделать tagged union, например kennytm делает здесь

struct TU {
   int type;
   union {
     int i;
     float f;
     std::string s;
   } u;

   TU(const TU& tu) : type(tu.type) {
     switch (tu.type) {
       case TU_STRING: new(&u.s)(tu.u.s); break;
       case TU_INT:    u.i = tu.u.i;      break;
       case TU_FLOAT:  u.f = tu.u.f;      break;
     }
   }
   ~TU() {
     if (tu.type == TU_STRING)
       u.s.~string();
   }
   ...
};

Который гарантирует, что правильный член уничтожен, или просто используйте std::variant или boost::variant

Ответ 2

Ваш пример не будет компилироваться. По умолчанию Unions имеют удаленный деструктор. Из-за чего, какой деструктор нужно назвать? Конечно, вы не можете назвать оба. И нигде не сохраняется какая-либо информация о том, какой элемент был фактически создан. Это зависит от вас, чтобы обеспечить надлежащий деструктор.

Здесь вывод GCC при попытке скомпилировать фрагмент кода:

In function ‘int main()’:
error: use of deleted function ‘main()::<anonymous union>::~<constructor>()’
       vector<int> vec; } s = { "Hello, world"s };
                                                ^

note: ‘main()::<anonymous union>::~<constructor>()’ is implicitly deleted because the default definition would be ill-formed:
      union { string str;
            ^

Ответ 3

Вам всегда нужно вручную вызвать конструктор объектов в вашей структуре с нетривиальными типами.

Обычно вам всегда нужно также явно их строить. Кажется странным, что назначение здесь работает.

В случае сомнений, хотя вы всегда можете проверить сборку, если вызываются деструкторы.

Сборка этого кода вызывает конструктор basic_string, но не деструктор. Итак, у вас будут утечки.

using namespace std;
int main(int argc, char** argv){
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

чтобы увидеть сборку: https://godbolt.org/g/wKu3vf