How can I construct (instead of assign) an std::array element?

117 Views Asked by At

I have a class that can't be re-assigned. The actual motive is that it has an std::variant member variable, and the possible types are not re-assignable, due to having some reference member variables (which can't be rebound). This is a very simple version of what we have:

class MyClass
{
public:
    MyClass() {}
    MyClass(int a) {}
    MyClass(MyClass &other) = default;
    MyClass(MyClass &&other) = default;
    MyClass &operator=(MyClass &) = delete;
    MyClass &operator=(MyClass &&) = delete;
};

Now, at some point, I have an array of these objects.

std::array<MyClass, 5> my_array; // default initializes all objects

Eventually, I want to make a new object and put it into the array.

my_array[1] = {69};

But std::array tried to assign, and so I get an error.

error: use of deleted function ‘MyClass& MyClass::operator=(MyClass&&)’
   29 |     my_array[1] = {69};
      |                      ^

Is there a way to force reconstructing instead of reassigning the element?

1

There are 1 best solutions below

0
fabian On

No there's no way to force a reconstruction that wouldn't be undefined behaviour, at least not without destroying the preexisting element and using placement new.

Since you mention std::variant though: You can change the element type, even after the element is constructed regardless of whether the element type of the std::variant is copyable/moveable. Just use std::variant::emplace.

The following example implements assignment operators in MyClass that make code similar to my_array[1] = {69}; work.

struct IntHolder
{
    int& m_value;
    IntHolder(int& value)
        : m_value(value)
    {
    }

};

using Variant = std::variant<std::monostate, IntHolder>;

class MyClass
{
    Variant m_member;
public:
    MyClass() {}
    MyClass(int& a)
    {
        m_member.emplace<IntHolder>(a);
    }

    // we'll remove both move and copy semantics for MyClass
    MyClass(MyClass&& other) = delete;
    MyClass& operator=(MyClass&&) = delete;

    MyClass& operator=(std::reference_wrapper<int> a)
    {
        m_member.emplace<IntHolder>(a);
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& s, MyClass const& value)
    {
        if (std::holds_alternative<IntHolder>(value.m_member))
        {
            s << "{ IntHolder { value = " << std::get<IntHolder>(value.m_member).m_value << " } }";
        }
        else
        {
            s << "{ std::monostate }";
        }
        return s;
    }
};


void Print(std::array<MyClass, 2> const& a)
{
    for (auto& e : a)
    {
        std::cout << e << '\n';
    }
}

int main()
{
    int x = 1;
    int y = 2;

    std::array<MyClass, 2> arr{ x, y };

    std::cout << "after initialization\n";
    Print(arr);

    // "swap"
    arr[0] = std::ref(y);
    arr[1] = std::ref(x);

    std::cout << "after modification\n";
    Print(arr);
}