私はVS2013とC ++ 11で作業しています。
カスタムのテンプレート化されたコレクションを実装しています。コレクションが容量を超えると、ストレージのサイズが変更されます。その時点で、データは古いストレージから新しいストレージに移動する必要があります。
データ要素Tに安全な移動セマンティクスを適用したいと思います。データ要素がリソースを所有している場合、リソースの所有権は元のストレージから盗まれ、新しいストレージに移動する必要があります。典型的なケースは、データ配列または他のリソースへの文字列またはポインタです。
明示的な移動コンストラクターと移動代入演算子を持ついくつかのデータ型があります。しかし、明示的な移動コンストラクター(DeepData1)を持つタイプ自体が、些細なコンストラクター(DeepData2)を持つ他のデータ構造体のメンバーである場合、バグが発生します。この記事の読み方によると、DeepData2で暗黙的なコンパイラー生成のmoveコンストラクターを取得する必要があると思います。http://en.cppreference.com/w/cpp/language/move_constructor
しかし、以下のサンプルでは、DeepData2の暗黙のコンストラクターに依存すると、ポインター_IMPORTANT_DATAの二重削除が原因でクラッシュすることを示しています。DeepData2のmoveコンストラクターを明示的にすると、コードは正常に実行されます。
私はそれをする必要がなく、暗黙の移動コンストラクターに依存できることを望んでいました。そうでなければ、追加のコンストラクターと割り当てを提供することを覚えておく必要があるのはユーザーコードの負担のようです。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;
}
MSVC2013は、生成または=default
移動コンストラクター(または代入演算子)をサポートしていません。
MSVC2015はそうします。実際のC ++ 11コンパイラであるために欠けている主なコンポーネントは、「式SFINAE」の失敗と呼ばれるものです。
MSVC2013およびC ++ 11での作業は、コンパイラーを交換しないと不可能です。C ++ 03とそれがサポートするC ++ 11の部分のハイブリッドでプログラミングできます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加