没有默认构造函数和已删除的复制构造函数初始化的对象的类成员数组

pek*_*u33 3 c++ c++11

我有一个名为'Container'的类,其中必须存储三个(在编译时已知)DontCopyMe类的对象.DontCopyMe类具有非默认构造函数和已删除的复制构造函数.如何初始化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;
}
Run Code Online (Sandbox Code Playgroud)

我当然得到了:

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"}}    
Run Code Online (Sandbox Code Playgroud)

Vit*_*meo 5

clang++ 3.6.2编译你的代码.g++ 5.2.0才不是.

根据这份报告,这是g ++中的一个错误.


这里有一个(讨厌)的解决方法,对于时刻:使用std::aligned_storage,placement new并且明确的析构函数调用:

#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];
};
Run Code Online (Sandbox Code Playgroud)

您必须正确地执行移动和复制操作以Containers清理对齐的存储.您可能希望将所有aligned_storage相关代码包装在辅助类中.


你提到你需要额外的安全性和常量.实现这一目标的最佳方法是包装std::aligned_storage一个帮助类,确保您不会犯错误.

这里有一些代码可以帮助您入门:

#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:
        // ...


};
Run Code Online (Sandbox Code Playgroud)

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

您还可以使用其他成员布尔数组跟踪初始化/取消初始化的项目(可以在发布版本中禁用,并且仅用于在开发期间验证类的正确用法).

如果你想要一个类似的(旧的)实现的例子,这是我为我的一个库编写的.

您还可以查看我编写的这个标记的union实现,以查看有关如何使用仅在调试版本中使用没有开销的成员变量以获得额外安全性的示例.

  • @peku33:不正确。它不会将对象放在堆上。`std::aligned_storage` 只是一堆字节。你可以把它们放在你想要的地方,在我的例子中,它们只是`Container`的一个成员。就内存而言,它相当于您想要的代码。你可以通过围绕 `std::aligned_storage` 创建一个包装类来解决你的 `const` 问题和你的 `initialization 问题`,确保你正确初始化/取消初始化内存。 (2认同)