简体   繁体   中英

Placement-new an STL container and destroying it safely afterwards

This code implements an unrestricted union which provides access by name and by index to any of its three members.

Since std::string is non-trivially constructed and destroyed, I need to provide special constructor and destructor for the union .

#include <iostream>
#include <string>

using namespace std ;

union MyUnion{
    string parts[3] ;
    struct{ string part1, part2, part3 ; } ;

    MyUnion(){
        new(parts+0) string ; //constructs the 3 strings in-place
        new(parts+1) string ;
        new(parts+2) string ;
    }
    ~MyUnion(){
        parts[0].~string() ; //calls string's destructor
        parts[1].~string() ;
        parts[2].~string() ;
    }
} ;

int main(){

    MyUnion u ;

    u.part1 = "one" ; //access by name
    u.part2 = "two" ;
    u.part3 = "three" ;

    cout << u.parts[0] << endl ; //access by index
    cout << u.parts[1] << endl ;
    cout << u.parts[2] << endl ;
}

This example compiles and works fine (seemingly), but my questions are:

  • Is it safe to do this?
  • Can I be sure that there will be no memory leaks?
  • What if string 's constructor throws an exception? Does that need to be caught so as to not try to destroy an object that was never constructed?

Note

The code compiles in VC2015, which does support unnamed structs. Please disregard that detail.

Is it safe to do this?

No. First, the common initial sequence rule only allows reading of members, not writing:

In a standard-layout union with an active member (9.3) of struct type T1 , it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2 ; the behavior is as if the corresponding member of T1 were nominated.

Secondly, common initial sequence is a trait of standard layout types:

The common initial sequence of two standard-layout struct (Clause 9) types is [...]

and std::string is not required to be standard-layout.

Is it safe to do this?

It depends on what you are willing to call safe. The code certainly invokes undefined behaviour by any reasonable interpretation of the standard.

You cannot read an inactive member of a union, except when there's a common subsequence involved (9.3 Unions), but these union members have no common initial sequence because the notion is only defined for two standard-layout structs (9.2 Class Members/20) and one member of the union is not a struct at all. It's an array so it cannot have a common initial sequence with anything.

This also applies to analogous code that uses primitive types, eg int x[3]; and struct {int x0, x1, x2}; . There's not even a guarantee that x2 and x[2] have the same address.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM