如何在模板数据上安全地使用std :: move()

时间:2016-02-25 14:52:01

标签: c++ c++11 rvalue-reference

我在VS2013和C ++ 11中工作。

我正在实现一个自定义的模板化集合。当集合超出容量时,它会调整存储大小。此时,数据应从旧存储移动到新存储。

我真的想在数据元素T上强制执行安全移动语义。如果数据元素拥有资源,则应从原始存储中窃取资源的所有权并移至新存储。典型情况可能是数据库或其他资源的字符串或指针。

我有几种具有显式移动构造函数和移动赋值运算符的数据类型。但是如果具有显式移动构造函数(DeepData1)的那些类型本身是具有普通构造函数(DeepData2)的其他数据结构,则会出现错误。根据我阅读本文的方式,我希望我应该在DeepData2上获得一个隐式编译器生成的移动构造函数。 http://en.cppreference.com/w/cpp/language/move_constructor

但是在下面的示例中,我展示了由于指针_IMPORTANT_DATA上的双重删除,依赖于DeepData2的隐式构造函数崩溃。如果我使DeepData2的移动构造函数显式,则代码运行正常。

我原本希望不需要这样做,并且能够依赖隐式移动构造函数。否则,用户代码似乎需要记住提供额外的构造函数和赋值。如果要求DeepData2肯定需要一个明确的移动构造函数,如果用户代码忘记提供一个构造函数,我是否可以将其作为错误?无论如何检测模板类型是否需要显式移动构造函数,因为具有显式移动构造函数的成员?当我使用std类型特征时,他们似乎没有给我足够的信息来写一个像“嘿用户代码,像模板arg T你需要移动语义而忘记”的体面断言

这很复杂。感谢您的任何建议或帮助

#include "stdafx.h"

#include <vector>
#include <algorithm>
#include <iostream>


template <typename T>
class DeepVector
{
public:
    DeepVector()
    {
        deepCopyResize(4);
    }

    void push_back(T& v)
    {
        if (_capacity <= _count)
        {
            deepCopyResize(_capacity * 2);
        }

        // !! deep copy desired here !!
        _data[_count++] = std::move(v);
    }

    T& operator[](int i) { return _data[i];  }

    void deepCopyResize(int cap)
    {
        int n = std::min(_count, cap);
        T* d = new T[cap];
        if (_data)
        {
            for (int i = 0; i < n; ++i)
            {
                // !! deep copy desired here !!
                d[i] = std::move(_data[i]);
            }
            delete[] _data;
        }
        _data = d; 
        _capacity = cap; 
        _count = n;
    }
private:

    int _capacity = 0;
    int _count = 0;
    T* _data = nullptr; 
};


struct FlatData1
{
    int x = 0, y = 0; 
};

struct DeepData1
{
    DeepData1()
    {

    }

    DeepData1(int s)
    {
        _size = s;
        _IMPORTANT_DATA = new int[_size];
    }

    // move constructor
    DeepData1(DeepData1&& rhs)
    {
        _size = rhs._size;
        _IMPORTANT_DATA = rhs._IMPORTANT_DATA;  // pilfer
        rhs._size = 0;
        rhs._IMPORTANT_DATA = nullptr;
    }

    // move operator
    DeepData1& operator=(DeepData1&& rhs)
    {
        _size = rhs._size;
        _IMPORTANT_DATA = rhs._IMPORTANT_DATA; // pilfer
        rhs._size = 0;
        rhs._IMPORTANT_DATA = nullptr;
        return *this;
    }

    ~DeepData1()
    {
        if (_IMPORTANT_DATA)
        {
            std::cout << "non-trivial destructor" << std::endl;
            _size = 0; 

            // it is an error to delete important twice
            delete[] _IMPORTANT_DATA;
            _IMPORTANT_DATA = NULL;
        }
    }
    int _size = 0; 
    int* _IMPORTANT_DATA = nullptr; 

    void resize(int s)
    {
        delete[] _IMPORTANT_DATA; 
        _IMPORTANT_DATA = new int[s];
        _size = s;
    }
};

struct DeepData2
{
    int z = 0; 
    DeepData1 problem;     // this data does not deep copy implicitly ?

    //  DeepData2() {} 

//   despite C++ standard forcing default not supported by VS2013
//    DeepData2(DeepData2&&) = default;


//    DeepData2(int s) : problem(s) {}
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // where are my implicit move constructors?

    // I have to uncomment these for the 
    // DeepData::operator=(DeepData&& rhs)
    // operator to be called 

    /*
     // have to manually implement move constructor?
     DeepData2(DeepData2&& rhs)
     {
     z = std::move(rhs.z);
     problem = std::move(rhs.problem);
     }

     // move operator
     DeepData2& operator=(DeepData2&& rhs)
     {
     z = std::move(rhs.z);
     problem = std::move(rhs.problem);
     return *this;
     }
   */

    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
};


int _tmain(int argc, _TCHAR* argv[])
{

    // ----------------------------------------------
    DeepVector<int> v1; 
    for (int i=0; i<5; ++i)
    { 
        v1.push_back(i);
    }

    if (v1[4] == 4)
    {
        std::cout << "resize 1 worked" << std::endl;
    }

    // ----------------------------------------------
    DeepVector<FlatData1> v2;
    for (int i = 0; i < 5; ++i)
    {
        v2.push_back(FlatData1());
        v2[i].x = i;
        v2[i].y = i;
    }

    if (v2[4].x == 4)
    {
        std::cout << "resize 2 worked" << std::endl;
    }

    // ----------------------------------------------
    DeepVector<DeepData1> v3;
    for (int i = 0; i < 5; ++i)
    {
        v3.push_back(DeepData1(10));

    }

    if (v3[4]._size == 10)
    {
        std::cout << "resize 3 worked" << std::endl;
    }


    // ----------------------------------------------


    bool b1 = std::is_move_constructible<DeepData1>();
    bool b2 = std::is_move_assignable<DeepData1>();
    bool b3 = std::is_trivially_move_assignable<DeepData1>();
    bool b4 = std::is_trivially_move_constructible<DeepData1>();

    bool b5 = std::is_move_constructible<DeepData2>();
    bool b6 = std::is_move_assignable<DeepData2>();

    // VS2013 says DeepData2 is trivially moveable with the implicit constructors
    bool b7 = std::is_trivially_move_assignable<DeepData2>();
    bool b8 = std::is_trivially_move_constructible<DeepData2>();

    DeepVector<DeepData2> v4;
    for (int i = 0; i < 5; ++i)
    {
        DeepData2 d2;
        d2.problem.resize(10);
        v4.push_back(d2); 
    }

    if (v4[4].problem._size == 10)
    {
        std::cout << "resize 4 worked" << std::endl;
    }


    return 0;
}

1 个答案:

答案 0 :(得分:2)

MSVC2013不支持生成的或=default移动构造函数(或赋值运算符)。

MSVC2015可以。它的主要缺失部分是实际的C ++ 11编译器,他们称之为&#34;表达SFINAE&#34;故障。

在不更换编译器的情况下,在MSVC2013和C ++ 11中工作是不可能的。您可以使用C ++ 03和它支持的C ++ 11部分进行混合编程。