本文介绍了没有默认构造函数和已删除副本构造函数初始化的对象的类成员数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为容器的类,其中必须存储三个DontCopyMe类的对象(在编译时已知)。 DontCopyMe类具有非默认构造函数和已删除的副本构造函数。

I have a class called 'Container' in which three (known at compile time) objects of class DontCopyMe must be stored. Class DontCopyMe has non-default constructor and deleted copy constructors. How can I initialize Containter?

示例代码:

#include <string>

class DontCopyMe
{
    public:
        DontCopyMe(const unsigned int SomeInt, const std::string & SomeString): SomeInt(SomeInt), SomeString(SomeString)
        {
        }
        DontCopyMe(const DontCopyMe &) = delete;
        DontCopyMe & operator = (const DontCopyMe &) = delete;
        DontCopyMe(DontCopyMe &&) = delete;
        DontCopyMe & operator = (DontCopyMe &&) = delete;

    private:
        const unsigned int SomeInt;
        const std::string SomeString;
};

class Container
{
    public:
        Container(): Array{{1, "A"}, {2, "B"}, {3, "C"}}
        {

        }

    private:
        DontCopyMe Array[3];
};

int main()
{
    Container C;
    return 0;
}

我当然会得到:

main.cpp: In constructor 'Container::Container()':
main.cpp:22:56: error: use of deleted function 'DontCopyMe::DontCopyMe(DontCopyMe&&)'
         Container(): Array{{1, "A"}, {2, "B"}, {3, "C"}}


推荐答案

clang ++ 3.6.2 编译您的代码。 g ++ 5.2.0 不会。

clang++ 3.6.2 compiles your code. g++ 5.2.0 does not.

这里是(讨厌)暂时的解决方法:使用,和显式析构函数调用:

Here's a (nasty) workaround, for the moment: use std::aligned_storage, placement new and explicit destructor calls:

#include <type_traits>

class Container
{
    private:
        using Storage = typename std::aligned_storage
        <
            sizeof(DontCopyMe),
            alignof(DontCopyMe)
        >::type;

    public:
        Container()
        {
            // Construct an instance of `DontCopyMe` in the memory
            // location starting at `&Array[0]`.
            new (&Array[0]) DontCopyMe{1, "A"};

            // ...
            new (&Array[1]) DontCopyMe{2, "B"};
            new (&Array[2]) DontCopyMe{3, "C"};

            // You can also (and should) use a for-loop.
        }

        ~Container()
        {
            // Interpret the bytes at location `&Array[2]` as if
            // they were a `DontCopyMe` instance, then call the
            // `~DontCopyMe()` destructor on it.
            (reinterpret_cast<DontCopyMe*>(&Array[2]))->~DontCopyMe();

            // ...
            (reinterpret_cast<DontCopyMe*>(&Array[1]))->~DontCopyMe();
            (reinterpret_cast<DontCopyMe*>(&Array[0]))->~DontCopyMe();

            // You can also (and should) use a for-loop.
        }

    private:
        Storage Array[3];
};

您必须对容器实施移动和复制操作正确清理对齐的存储。您可能需要将所有与 aligned_storage 相关的代码包装在帮助器类中。

You'll have to implement move and copy operations for Containers that cleanup the aligned storage properly. You may want to wrap all aligned_storage-related code in an helper class.

您提到您需要额外的安全性和稳定性。达到此目的的最佳方法是将 std :: aligned_storage 包装在帮助程序类中,以确保您不会出错。

You mentioned you require additional safety and constness. The best way to achieve this is wrapping std::aligned_storage in an helper class that will make sure you don't make mistakes.

以下代码可帮助您入门:

Here's some code to get you started:

#include <type_traits>
#include <utility>
#include <cassert>

template<typename T, std::size_t TSize>
struct ASImmutableArray
{
    private:
        using ThisType = ASImmutableArray<T, TSize>;

        using Storage = typename std::aligned_storage
        <
            sizeof(T),
            alignof(T)
        >::type;

        Storage data[TSize];

        template<typename... Ts>
        void initAt(std::size_t mIndex, Ts&&... mXs)
        {
            assert(mIndex >= 0 && mIndex < TSize);
            // assert the data was not initialized
            new (&data[mIndex]) T(std::forward<Ts>(mXs)...);
        }

        void deinitAt(std::size_t mIndex)
        {
            assert(mIndex >= 0 && mIndex < TSize);
            // assert the data was actually initialized
            reinterpret_cast<T*>(&data[mIndex])->~T();
        }

    public:
        // ...


};

一个想法正在传递 std :: tuple ASImmutableArray 的构造函数中创建实例,并使用 placement new T 实例。通过扩展元组并将其内容转发到 T 的构造函数,从而使正确的索引处的code>。元组将包含与构造 T 所需的类型相同的类型。

An idea is passing std::tuple instances in the constructor of ASImmutableArray, and creating T instances in place using placement new at the correct indices by expanding the tuples and forwarding their contents to T's constructor. The tuples would contain the same types as the types required to construct T.

您还可以跟踪已初始化/带有附加成员布尔数组的初始化项目(可以在发行版本中禁用,并且只能用于在开发过程中验证类的正确用法)。

You can also keep track of initialized/deinitialized items with an additional member boolean array (that can be disabled in release builds, and only used for verifying the correct usage of the class during development).

想要一个类似的(旧)实现的示例,

If you want an example of an (old) implementation of something similar, this is something I've written for one of my libraries.

您也可以查看,以查看有关如何使用仅调试的成员变量的示例,该成员变量在发行版本中没有额外的开销。

You can also check out this tagged union implementation I've written to see an example on how I use debug-only member variables that have no overhead in release-builds for additional safety.

这篇关于没有默认构造函数和已删除副本构造函数初始化的对象的类成员数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-22 08:24