60 votos

¿Por qué no mover-asignación/movimiento-constructor predeterminado?

Soy un simple programador. Mi clase de miembros de las variables más a menudo consiste en POD-tipos y STL-contenedores. Debido a esto yo rara vez tienen que escribir operadores de asignación o copia de los constructores, ya que estos se implementan de forma predeterminada.

Añadir a esto, si yo uso std::move sobre los objetos no se mueven, se utiliza la asignación de operador, lo que significa std::move es perfectamente seguro.

Como yo soy un simple programador, me gustaría aprovechar el movimiento de las capacidades de sin la adición de un movimiento constructor/operador de asignación para cada clase escribo, ya que el compilador simplemente podría aplicarse como "this->member1_ = std::move(other.member1_);..."

Pero no (al menos no en Visual 2010), ¿hay alguna razón especial para ello?

Lo que es más importante; ¿hay alguna manera de evitar esto?

Actualización: Si se mira hacia abajo en GManNickG la respuesta que proporciona una gran macro para esto. Y si usted no lo sabe, en el caso de implementar mover-semántica puede quitar la función de miembro intercambio.

55voto

James McNellis Puntos 193607

El implícita la generación de mover los constructores y operadores de asignación ha sido polémica y ha habido modificaciones importantes en los últimos borradores del Estándar de C++, por lo que actualmente disponibles, los compiladores probable que se comportan de manera diferente con respecto a la implícito generación.

Para más información sobre la historia de la cuestión, véase el 2010 WG21 papeles lista y busque "mov"

La especificación actual (N3225, a partir de noviembre) estados (N3225 12.8/8):

Si la definición de una clase X no declara explícitamente un movimiento constructor, se ha declarado implícitamente como defecto si y sólo si

  • X no tiene un usuario, declaró el constructor de copia, y

  • X no tiene un usuario, declaró copia operador de asignación,

  • X no tiene un usuario, declaró mover operador de asignación,

  • X no tiene un usuario, declaró destructor, y

  • el movimiento constructor no sería implícitamente definido como eliminado.

Hay un lenguaje similar en 12.8/22 especificando cuando el movimiento operador de asignación es implícitamente declarado en mora. Usted puede encontrar la lista completa de los cambios realizados para apoyar la especificación de la corriente implícito mover generación en N3203: Endurecimiento de las condiciones para la generación implícita se mueve , que se basa principalmente en una de las resoluciones propuestas por Bjarne Stroustrup en papel de N3201: Mueve a la derecha a lo largo.

8voto

Jerry Coffin Puntos 237758

Implícitamente se genera mover los constructores que se han considerado para el estándar, pero pueden ser peligrosos. Ver a Dave Abrahams del análisis.

En la final, sin embargo, el estándar incluye implícita la generación de mover los constructores y mover los operadores de asignación, aunque con bastante considerable lista de limitaciones:

Si la definición de una clase X no declarar explícitamente un movimiento constructor, se ha declarado implícitamente como defecto si y sólo si
- X no tiene un usuario, declaró el constructor de copia,
- X no tiene un usuario, declaró copia operador de asignación,
- X no tiene un usuario, declaró mover operador de asignación,
- X no tiene un usuario, declaró destructor, y
- el movimiento constructor no sería implícitamente definido como eliminado.

Eso no es todo lo que hay a la historia, aunque. Un cto r puede ser declarada, pero todavía se define como eliminar:

Un implícitamente-declaró copiar/mover constructor de una línea pública de los miembros de su clase. Un defecto de copiar/mover constructor para una clase X se define como eliminado (8.4.3) si X tiene:

- una variante miembro con un no-trivial constructor correspondiente y X es una unión como la clase,
- un no-miembro de datos estático de tipo de clase M (o matriz de la misma), que no puede ser copiado/movido debido a que la resolución de sobrecarga (13.3), aplicada a la M del constructor correspondiente, los resultados de una ambigüedad o una función que es eliminados o inaccesibles desde el impago del constructor,
- directa o virtual de la clase base B que no puede ser copiado/movido debido a que la resolución de sobrecarga (13.3), aplicada a la B del constructor correspondiente, los resultados de una ambigüedad o una función que es eliminados o inaccesibles desde el impago del constructor,
- cualquier daño directo o virtual de la clase base o no miembro de datos estático de un tipo con un destructor que es eliminados o inaccesibles desde el impago del constructor,
- para el constructor de copia, que no es un miembro de datos estático de r-value tipo de referencia, o
- para mover el constructor, que no es un miembro de datos estático o directa o virtual de la clase base con un tipo que no tiene un movimiento constructor y no es trivial copiable.

7voto

GManNickG Puntos 155079

(por ahora, estoy trabajando en un estúpido macro...)

Sí, fui de esa ruta. Aquí está la macro:

// detail/move_default.hpp
#ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP
#define UTILITY_DETAIL_MOVE_DEFAULT_HPP

#include <boost/preprocessor.hpp>

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE(pR, pData, pBase) pBase(std::move(pOther))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE(pR, pData, pBase) pBase::operator=(std::move(pOther));

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR(pR, pData, pMember) pMember(std::move(pOther.pMember))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT(pR, pData, pMember) pMember = std::move(pOther.pMember);

#define UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        ,                                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)                                                   \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#endif

// move_default.hpp
#ifndef UTILITY_MOVE_DEFAULT_HPP
#define UTILITY_MOVE_DEFAULT_HPP

#include "utility/detail/move_default.hpp"

// move bases and members
#define UTILITY_MOVE_DEFAULT(pT, pBases, pMembers) UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)

// base only version
#define UTILITY_MOVE_DEFAULT_BASES(pT, pBases) UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)

// member only version
#define UTILITY_MOVE_DEFAULT_MEMBERS(pT, pMembers) UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)

#endif

(He quitado los comentarios reales, que son la longitud y el documental.)

Se especifica en las bases y/o los miembros de tu clase como un preprocesador lista, por ejemplo:

#include "move_default.hpp"

struct foo
{
    UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str));

    int x;
    std::string str;
};

struct bar : foo, baz
{
    UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz));
};

struct baz : bar
{
    UTILITY_MOVE_DEFAULT(baz, (bar), (ptr));

    void* ptr;
};

Y sale un constructor y se mueven-operador de asignación.

(En un aparte, si alguien sabe cómo podría combinar los detalles en una macro, que sería oleaje.)

3voto

Puppy Puntos 90818

VS2010 no lo hace porque no eran estándar en el momento de aplicación.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X