Позвольте мне предисловие, сказав, что я прочитал некоторые из многих уже заданных вопросов относительно семантики перемещения. Этот вопрос заключается не в том, как использовать семантику перемещения, она спрашивает, какова цель этого - если я не ошибаюсь, я не понимаю, зачем нужна семантика.
Фон
Я выполнял тяжелый класс, который для целей этого вопроса выглядел примерно так:
class B;
class A
{
private:
std::array<B, 1000> b;
public:
// ...
}
Когда пришло время сделать оператор присваивания перемещения, я понял, что могу значительно оптимизировать процесс, изменив член b
на std::array<B, 1000> *b;
- тогда перемещение может быть просто удалением и указателем swap.
Это привело меня к следующей мысли: теперь не должны все не примитивные члены типа быть указателями для ускорения движения (исправлено ниже [1] [2]) (есть случай для случаев, когда память не должна динамически распределяться, но в этих случаях оптимизация движения не является проблемой, поскольку нет способа сделать это)?
Вот где у меня была следующая реализация: зачем создавать класс A
, который на самом деле просто содержит указатель b
, поэтому замена позже проще, когда я могу просто сделать указатель на весь класс A
. Очевидно, что если клиент ожидает, что движение будет значительно быстрее, чем копирование, клиент должен быть в порядке с распределением динамической памяти. Но в этом случае, почему клиент не просто динамически выделяет весь класс A
?
Вопрос
Не может ли клиент уже использовать указатели, чтобы сделать все, что переносит семантику? Если да, то в чем смысл семантики перемещения?
Переместить семантику:
std::string f()
{
std::string s("some long string");
return s;
}
int main()
{
// super-fast pointer swap!
std::string a = f();
return 0;
}
Указатели
std::string *f()
{
std::string *s = new std::string("some long string");
return s;
}
int main()
{
// still super-fast pointer swap!
std::string *a = f();
delete a;
return 0;
}
И здесь сильное задание, которое все говорят, настолько велико:
template<typename T>
T& strong_assign(T *&t1, T *&t2)
{
delete t1;
// super-fast pointer swap!
t1 = t2;
t2 = nullptr;
return *t1;
}
#define rvalue_strong_assign(a, b) (auto ___##b = b, strong_assign(a, &___##b))
Изобразительное - последнее в обоих примерах может считаться "плохим стилем" - что бы это ни значило - но действительно ли это стоит всех проблем с двойными амперсандами? Если исключение может быть выброшено до вызова delete a
, это все еще не является реальной проблемой - просто сделайте охрану или используйте unique_ptr
.
Изменить [1] Я просто понял, что это не будет необходимо для классов, таких как std::vector
, которые сами используют распределение динамической памяти и имеют эффективные методы перемещения. Это просто умаляет мысль, которую я имел - вопрос ниже все еще стоит.
Изменить [2] Как упоминалось в обсуждении в комментариях и ответах ниже, весь этот вопрос довольно спорный. Следует использовать семантику значения как можно больше, чтобы избежать накладных расходов на распределение, поскольку клиент всегда может переместить все это в кучу, если это необходимо.