如何构造(而不是分配)std::array元素?

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

提问人:BlueMoon93 提问时间:7/29/2023 更新时间:7/29/2023 访问量:101

问:

我有一个无法重新分配的班级。实际动机是它有一个成员变量,并且由于有一些引用成员变量(不能重新组合),可能的类型是不可重新分配的。这是我们所拥有的非常简单的版本:std::variant

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

现在,在某个时候,我有了一个这些对象的数组。

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

最终,我想创建一个新对象并将其放入数组中。

my_array[1] = {69};

但是std::array试图分配,所以我得到一个错误。

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

有没有办法强制重建而不是重新分配元素?

C++ 构造函数 赋值运算符 stdarray

评论

3赞 Some programmer dude 7/29/2023
处理的数组与任何其他数组一样。所有元素都是在创建数组时构造的。您所能做的就是将新值复制或移动到单个(和已经存在的)元素中。std::array
0赞 Some programmer dude 7/29/2023
或者,虽然我真的不推荐它,但您可以使用 placement-new 来构造一个新对象来代替元素中已经存在的对象。它将强制您显式处理对象生存期,并跟踪您是否已经进行了放置-new。这将使您的代码复杂化。作为一种有点类似(但仍然不推荐)的解决方法,您可以使用智能指针作为元素类型。
0赞 user7860670 7/29/2023
std::array<MyClass, 5> my_array{1, 2, 3, 4, 5};
2赞 chrysante 7/29/2023
为什么你的类中有复制和移动构造函数,但没有同化操作?如果你问我,这个设计只是在自找麻烦。3 规则/5 规则的存在是有原因的。
1赞 Some programmer dude 7/29/2023
编辑您的问题,以向我们询问实际和潜在的问题。现在你的问题是一个XY问题

答:

1赞 fabian 7/29/2023 #1

不,没有办法强制重建不是未定义的行为,至少在不破坏预先存在的元素并使用放置新的情况下。

既然你提到:你可以改变元素类型,即使在元素被构造之后,无论元素类型是否是可复制的/可移动的。只需使用 std::variant::emplacestd::variantstd::variant

下面的示例实现赋值运算符,使代码类似于 work。MyClassmy_array[1] = {69};

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