Using a union with unique_ptr

c++, c++11, struct, unions, unique-ptr

Solution

For unrestricted union, you have to manage yourself some construct/destruction.

Following may help:

union myUnion{
    std::unique_ptr<float> upFloat;
    std::unique_ptr<int> upInt;

    myUnion(){ new (&upFloat) std::unique_ptr<float>{};}
    ~myUnion() {}
};

class myStruct
{
public:
    ~myStruct()
    {
        destroy();
    }

    void destroy()
    {
        switch (type)
        {
            case unionType::f: num.upFloat.~unique_ptr<float>(); break;
            case unionType::i: num.upInt.~unique_ptr<int>(); break;
        }
    }

    void set(std::unique_ptr<int> p)
    {
        destroy();
        new (&num.upInt) std::unique_ptr<int>{std::move(p)};
        type = unionType::i;
    }
    void set(std::unique_ptr<float> p)
    {
        destroy();
        new (&num.upFloat) std::unique_ptr<float>{std::move(p)};
        type = unionType::f;
    }

public:
    enum class unionType {f, i} type = unionType::f; // match the default constructor of enum
    myUnion num;
};

int main()
{
    myStruct aStruct, bStruct;

    aStruct.set(std::make_unique<float>(3.14f));
    bStruct.set(std::make_unique<int>(3));

    std::cout << "aStruct float = " << *aStruct.num.upFloat << std::endl;
    std::cout << "bStruct int = " << *bStruct.num.upInt << std::endl;
    return 0;
}

In C++17, you may use `std::variant` instead of your own struct

Problem

Trying to use a unique_ptr inside a union gives me a segfault when I try to std::move or std::make_unique it. ``` #include <iostream> #include <memory> union myUnion{ struct{std::unique_ptr<float> upFloat;}structUpFloat; struct{std::unique_ptr<int> upInt;}structUpInt; myUnion(){} ~myUnion(){} }; struct myStruct{ int x; myUnion num; }; int main() { myStruct aStruct, bStruct; aStruct.x = 1; bStruct.x = 2; auto upF = std::make_unique<float>(3.14); auto upI = std::make_unique<int>(3); aStruct.num.structUpFloat.upFloat = std::move(upF); bStruct.num.structUpInt.upInt = std::move(upI); std::cout << "aStruct float = " << *aStruct.num.structUpFloat.upFloat << std::endl; std::cout << "bStruct int = " << *bStruct.num.structUpInt.upInt << std::endl; return 0; } ``` However, using a normal pointer works as expected: ``` #include <iostream> #include <memory> union myUnion{ struct{float *pFloat;}structPFloat; struct{int *pInt;}structPInt; myUnion(){} ~myUnion(){} }; struct myStruct{ int x; myUnion num; }; int main() { myStruct aStruct, bStruct; aStruct.x = 1; bStruct.x = 2; auto upF = std::make_unique<float>(3.14); auto upI = std::make_unique<int>(3); aStruct.num.structPFloat.pFloat = upF.get(); bStruct.num.structPInt.pInt = upI.get(); std::cout << "aStruct float = " << *aStruct.num.structPFloat.pFloat << std::endl; std::cout << "bStruct int = " << *bStruct.num.structPInt.pInt << std::endl; return 0; } ``` This is using clang.3.4.2 or gcc.4.9.0. So I'm assuming that I am doing something wrong here. Any help would be appreciated. EDIT: Ok, so it's probably a nice thing to do to share the code I settled on. Big thanks to everyone who pointed me to managing the lifetime of my pointers in variant members using placement new. ``` #include <memory> #include <iostream> #include <vector> struct myStruct { public: union { std::unique_ptr<float> upFloat; std::unique_ptr<int> upInt; }; enum class unionType {f, i,none} type = unionType::none; // Keep it sane myStruct(){} myStruct(std::unique_ptr<float> p) { new (&upFloat) std::unique_ptr<float>{std::move(p)}; type = unionType::f; } myStruct(std::unique_ptr<int> p) { new (&upInt) std::unique_ptr<int>{std::move(p)}; type = unionType::i; } ~myStruct() { switch (type) { case unionType::f: upFloat.~unique_ptr<float>(); break; case unionType::i: upInt.~unique_ptr<int>(); break; } } }; int main() { std::vector<std::unique_ptr<myStruct>> structVec; structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(3.14f))); structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(739))); structVec.push_back(std::make_unique<myStruct>()); structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(8.95f))); structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(3))); structVec.push_back(std::make_unique<myStruct>()); for(auto &a: structVec) { if(a->type == myStruct::unionType::none) { std::cout << "Struct Has Unallocated Union" << std::endl; } else if(a->type == myStruct::unionType::f) { std::cout << "Struct float = " << *a->upFloat << std::endl; } else { std::cout << "Struct int = " << *a->upInt << std::endl; } std::cout << std::endl; } return 0; } ``` Outputs: Struct float = 3.14 Struct int = 739 Struct Has Unallocated Union Struct float = 8.95 Struct int = 3 Struct Has Unallocated Union

Original source

Related problems