Для индивидуального использования я унаследовал std::vector
на заказ class Vector
, Для моего требования это public
наследство в порядке.
Одно из намерений состоит в том, чтобы избежать многократного создания копий векторного массива, поэтому я решил, что этот пользовательский класс основан на владении.
Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // vA2 refers to original A[100] and ...
// ... vA1 is now blank which is expected
Вот как это реализовано для C ++ 03:
template<typename T>
struct Vector : std::vector<T>
{
// ... other constructors
Vector (const Vector ©) // copy constructor
{
this->swap(const_cast<Vector&>(copy)); // <---- line of interest
}
// 'operator =' is TBD (either same as above or unimplemented)
};
Я нарушаю какое-либо языковое правило / особенность / соглашение? В этом коде есть что-то плохое?
редактировать: Я добавил свой новый подход в форме ответа ниже (вот его работа демонстрация).
Я думаю, что вы ищете семантику перемещения.
Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = std::move(vA1); // vA2 refers to original A[100] and ...
// ... vA1 is now blank which is expected
Если ваш компилятор не поддерживает семантику перемещения, я бы посоветовал вам использовать умные указатели или взглянуть на повышение :: движение который может эмулировать семантику перемещения в компиляторах до C ++ 11.
std::shared_ptr<Vector<A>> vA1(new Vector<A>(100)); // vA1 is allocated A[100]
std::shared_ptr<Vector<A>> vA2 = vA1; // vA2 refers to original A[100] and ...
vA1.reset();
// ... vA1 is now blank which is expected
или даже проще:
Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2;
vA1.swap(vA2);// vA2 refers to original A[100] and ...
// ... vA1 is now blank which is expected
Вы действительно должны взглянуть на использование C ++ 11: все стандартные контейнеры являются подвижными и, таким образом, во многих случаях напоминают вашу семантику, а именно, когда известно, что право собственности может быть передано. Точнее, контейнеры перемещаются, а не копируются при возврате объектов из функции или при использовании временного объекта для создания или назначения объекта. Попытка воспроизвести этот механизм каким-либо образом почти наверняка приведет к неожиданному поведению в некоторых случаях.
В C ++ 11 подвижная семантика интегрирована в язык, и функции, включая конструкторы и присваивания, могут быть перегружены, чтобы вести себя по-разному в зависимости от того, является ли объект подвижным или нет.
Ваш код нарушает соглашения, которые поддерживают константность:
const Vector<int> foo(12);
const Vector<int> bar(foo); // UB.
some_function_that_takes_Vector_by_value(bar); // UB.
Вы можете сказать: «но никто никогда не создаст const
экземпляр Vector
, поэтому UB никогда не произойдет «. В этом случае вы могли бы по крайней мере заставить свой экземпляр ctor использовать неконстантную ссылку, чтобы следовать соглашению о том, что функции не должны изменять объекты, взятые const&
параметры.
Даже тогда лично я бы предпочел потребовать, чтобы пользователь «включил» скорость путем явной замены, чем создавать для них подобные ловушки. Я также предпочел бы вообще не иметь копирующего ctor, чем тот, который ведет себя в отличие от обычной копии. Если люди будут иметь в виду, что Vector
на самом деле не может быть скопировано, тогда они не должны использовать синтаксис копирования, чтобы не копировать его.
Проблема ненужных копий в C ++ 03 не нова, и IMO не требует нового решения. Многие программисты на C ++ уже знают, как воспользоваться swap
и копируйте elision, и тем, кто не знает, как избежать ненужных копий, лучше научиться делать это обычным способом, чем научиться делать это уникальным способом, требуемым вашим конкретным классом.
С учетом комментариев / ответов, я понял, что мой нынешний подход не очень хорошая идея, чтобы иметь дело с вещами.
Я придумал измененный дизайн, в котором я пытаюсь смоделировать операцию «перемещение», используя swap()
метод (считают их взаимозаменяемыми в этом контексте).
Для любого класса или контейнера, который хочет сделать более быстрое копирование (т.е. перемещать путем замены), следует наследовать ниже класса (непосредственно копировать вставку из реального файла):
/*
* An empty CRTP base class for the classes which are allowing move sematics (effectively swap)
* A given class `X` must implement `void swap(X&);` method for the movement (i.e. content swapping)
* For example, `X` is deriving this class (i.e. `class X : public Movable<X> ...`)
* ... below is an illustration for how to transfer contents of an `X` object to another
* `X o1; ... X o2(o1.move()); // contents of o1 is now moved to o2`
*/
template<typename Derived>
class Movable
{
/*
* Empty constructor for normal behavior
*/
public: Movable ()
{}
/*
* Copy constructor which does nothing; without this compiler errors out !
*/
public: Movable (const Movable ©)
{}
/*
* Move constructor which will effectively call `Derived::swap()` method
* After this, the contents of the object will be moved to each other
* For syntactic sugar, it has been made `explicit`
*/
public: explicit Movable (Movable &orig)
{
(*this)(orig);
}
/*
* Moving while Not initializing object, one need to use `operator ()` and not `operator =`
* If `operator =` is used then simply `move()` part will not happen; i.e. below 2 are same:
* `obj1 = obj2.move();` and `obj1 = obj2;`
*/
public: void operator () (Movable &orig)
{
static_cast<Derived*>(this)->swap(static_cast<Derived&>(orig));
}
/*
* This method is called from `Derived` class when move sematics is intended
*/
public: Movable& move ()
{
return *this;
}
};
Вот как это должно быть развернуто:
template<typename T>
struct Vector : std::vector<T>,
Movable<Vector<T> > // inherit as CRTP base
{
// ... other methods
typedef Movable<Vector<T> > Movable_;
Vector (Movable_ &orig) : Movable_(orig) {} // Declare a constructor
};
Вот как это должно быть использовано:
Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // normal copying (no change)
vA2 = vA1; // normal assignment (no change)
Vector<A> vA3(vA1.move()); // <------- "moves" contents from 'vA1' to 'vA3'
vA1(vA2.move()); // <------- "moves" contents from 'vA2' to 'vA1'
vA2 = vA3.move(); // normal copying from 'vA3' to 'vA2' ('vA3' unaffected)