如何安全地在模板数据上使用 std::move()
How to safely use std::move() on template data
我在VS2013和C++11工作。
我正在实现一个自定义模板化集合。当集合超出容量时,它会调整其存储大小。 此时,数据应从旧存储移动到新存储。
我真的很想在数据元素 T 上强制实施安全移动语义。 如果数据元素拥有资源,则应从原始存储中窃取资源的所有权并移动到新存储。 典型情况可能是指向数据数组或其他资源的字符串或指针。
我有几种数据类型具有显式移动构造函数和移动赋值运算符。 但是,如果那些具有显式移动构造函数(DeepData1)的类型本身是具有琐碎构造函数(DeepData2)的其他数据结构的成员,则会出现错误。 根据我阅读本文的方式,我希望我应该在 DeepData2 上获得一个隐式编译器生成的移动构造函数。http://en.cppreference.com/w/cpp/language/move_constructor
但是在下面的示例中,我展示了依赖 DeepData2 的隐式构造函数会由于指针_IMPORTANT_DATA上的双重删除而崩溃。 如果我显式使用 DeepData2 的移动构造函数,代码运行良好。
我希望不需要这样做,并且能够依赖隐式移动构造函数。另一方面,用户代码似乎必须记住提供额外的构造函数和赋值。如果要求 DeepData2 肯定需要一个显式移动构造函数,如果用户代码忘记提供一个,我会让它成为错误吗? 是否有办法检测模板类型是否需要显式移动构造函数,因为具有显式移动构造函数的成员? 当我使用 std 类型特征时,他们似乎没有给我足够的信息来写一个像样的断言,比如"嘿用户代码,对于模板参数 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;
}
生成或=default
移动构造函数(或赋值运算符)。
MSVC2015确实如此。 它的主要缺失组件是实际的C++11编译器,他们称之为"表达式SFINAE"失败。
如果不更换编译器,就无法在 MSVC2013 和 C++11 中工作。 您可以在 C++03 及其支持的 C++11 部分的混合中编程。
- Usages of std::move
- 在C++中对T*类型执行std::move的意外行为
- 关于std::move的使用,是否有编译警告
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 通过实例理解std::move及其目的
- 返回一个带有 std::move 的对象并链接函数
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- std::move a const std::vector in a lambda capture
- "std::forward"和"std::move"真的不生成代码吗?
- 如何在没有 std::move 的情况下移动临时对象
- 关于在成员重载中使用 std::move() 的问题
- 显式清除 std::move 之后的源资源
- std::move 如何使原始变量的值无效?
- 复制elision、std::move和链式函数调用
- 如果真的需要std::move,我们应该什么时候声明右值refs
- 使用std::move将std::unique_ptr作为qt信号参数传递
- 无法使用带有 std::move 的自定义删除器插入 std::unique_ptr
- C++:我应该在 return 语句中显式使用 std::move() 来强制移动吗?
- 编译器是否足够聪明,以至于 std::move 变量超出范围?
- std::move() 或其在局部变量上的显式等价物可以允许 elision 吗?