¿Por qué tener movimiento semántico?

Permítanme comenzar diciendo que he leído algunas de las muchas preguntas ya formuladas con respecto a la semántica de movimientos. Esta pregunta no es sobre cómo usar la semántica de movimientos, es preguntarse cuál es su propósito: si no me equivoco, no veo por qué se necesita la semántica de movimientos.

Fondo

Estaba implementando una clase pesada, que, a los efectos de esta pregunta, se parecía a esto:

class B;

class A
{
private:
    std::array<B, 1000> b;
public:
    // ...
}

Cuando llegó el momento de hacer un operador de asignación de movimiento, me di cuenta de que podía optimizar significativamente el proceso cambiando elb miembro destd::array<B, 1000> *b; - Entonces el movimiento podría ser simplemente un borrado y un cambio de puntero.

Esto me llevó al siguiente pensamiento: ahora,no todos los miembros de tipo no primitivo deben ser indicadores para acelerar el movimiento (corregidos a continuación [1] [2]) (¿Hay casos en los que no se deba asignar dinámicamente la memoria, pero en estos casos, la optimización del movimiento no es un problema, ya que no hay manera de hacerlo)?

Aquí es donde tuve la siguiente realización: ¿por qué crear una clase?A que en realidad solo alberga un punterob por lo tanto, cambiar más tarde es más fácil cuando puedo simplemente hacer un puntero a la totalidadA clase en si Claramente, si un cliente espera que el movimiento sea significativamente más rápido que copiar, el cliente debería estar bien con la asignación de memoria dinámica. Pero en este caso, ¿por qué el cliente no solo asigna dinámicamente el conjunto?A ¿clase?

La pregunta

¿No puede el cliente aprovechar los punteros para hacer todo lo que la semántica nos da? Si es así, entonces ¿cuál es el propósito de mover la semántica?

Mover la semántica:

std::string f()
{
    std::string s("some long string");
    return s;
}

int main()
{
    // super-fast pointer swap!
    std::string a = f();
    return 0;
}

Punteros:

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;
}

Y aquí está la tarea fuerte que todos dicen que es tan grande:

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))

Bien, lo último en ambos ejemplos puede considerarse "mal estilo", lo que sea que signifique, pero ¿realmente vale la pena todos los problemas con los símbolos dobles? Si una excepción puede ser lanzada antesdelete a se llama, eso todavía no es un problema real, solo haga un protector o useunique_ptr.

Editar [1] Me di cuenta de que esto no sería necesario con clases comostd::vector que utilizan la asignación dinámica de memoria y tienen métodos de movimiento eficientes. Esto simplemente invalida un pensamiento que tenía, la pregunta que sigue a continuación sigue en pie.

Editar [2] Como se mencionó en la discusión en los comentarios y respuestas a continuación, todo este punto es bastante discutible. Se debe utilizar la semántica de valor tanto como sea posible para evitar la sobrecarga de asignación, ya que el cliente siempre puede mover todo el asunto al montón si es necesario.

Respuestas a la pregunta(4)

Su respuesta a la pregunta