c++ - studio - para que sirve un constructor en c#
¿Por qué no mover-asignación/mover-constructor por defecto? (4)
Soy un programador simple. Las variables de miembros de mi clase generalmente consisten en POD-types y STL-containers. Debido a esto, rara vez tengo que escribir operadores de asignación o copiar constructores, ya que estos se implementan por defecto.
Agregue a esto, si uso std::move
en objetos no movibles, utiliza el operador de asignación, lo que significa que std::move
es perfectamente seguro.
Como soy un simple programador, me gustaría aprovechar las capacidades de movimiento sin agregar un constructor de movimientos / operador de asignación a cada clase que escribo, ya que el compilador podría simplemente implementarlos como " this->member1_ = std::move(other.member1_);...
"
Pero no es así (al menos no en Visual 2010), ¿hay alguna razón en particular para esto?
Más importante; ¿Hay alguna forma de evitar esto?
Actualización: si miras hacia abajo a la respuesta de GManNickG, él proporciona una gran macro para esto. Y si no lo sabía, si implementa semántica de movimiento puede eliminar la función de miembro de intercambio.
(por ahora, estoy trabajando en una macro estúpida ...)
Sí, fui esa ruta también. Aquí está tu 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
1.
// 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
(Eliminé los comentarios reales, que son de larga duración y documentales).
Usted especifica las bases y / o miembros en su clase como una lista de preprocesadores, 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 operador de movimiento y operador de asignación de movimiento.
(Como comentario, si alguien sabe cómo podría combinar los detalles en una macro, sería genial).
La generación implícita de constructores de movimientos y operadores de asignación ha sido polémica y ha habido revisiones importantes en los borradores recientes del Estándar C ++, por lo que los compiladores actualmente disponibles probablemente se comportarán de manera diferente con respecto a la generación implícita.
Para obtener más información sobre la historia del problema, consulte la lista de documentos del WG21 2010 y busque "mov"
La especificación actual (N3225, de noviembre) establece (N3225 12.8 / 8):
Si la definición de una clase
X
no declara explícitamente un constructor de movimiento, se declarará implícitamente como predeterminado si y solo si
X
no tiene un constructor de copia declarado por el usuario, y
X
no tiene un operador de asignación de copia declarado por el usuario,
X
no tiene un operador de asignación de movimiento declarado por el usuario,
X
no tiene un destructor declarado por el usuario, yel constructor de movimiento no se definiría implícitamente como eliminado.
Hay un lenguaje similar en 12.8 / 22 que especifica cuándo el operador de asignación de movimiento se declara implícitamente como predeterminado. Puede encontrar la lista completa de cambios realizados para admitir la especificación actual de generación de movimiento implícita en N3203: Ajustar las condiciones para generar movimientos implícitos , que se basó en gran medida en una de las resoluciones propuestas por el documento N3201 de Bjarne Stroustrup : Avanzar a la derecha .
Los constructores de movimientos generados implícitamente se han considerado para el estándar, pero pueden ser peligrosos. Vea el analysis Dave Abrahams.
Al final, sin embargo, el estándar sí incluía la generación implícita de constructores de movimiento y operadores de asignación de movimiento, aunque con una lista bastante importante de limitaciones:
Si la definición de una clase X no declara explícitamente un constructor de movimiento, se declarará implícitamente como predeterminado si y solo si
- X no tiene un constructor de copia declarado por el usuario,
- X no tiene un operador de asignación de copia declarado por el usuario,
- X no tiene un operador de asignación de movimiento declarado por el usuario,
- X no tiene un destructor declarado por el usuario, y
- el constructor de movimientos no se definiría implícitamente como eliminado.
Sin embargo, eso no es todo lo que hay en la historia. Un ctor se puede declarar, pero aún se define como eliminado:
Un constructor de copia / movimiento implícitamente declarado es un miembro público en línea de su clase. Un constructor de copia / movimiento predeterminado para una clase X se define como eliminado (8.4.3) si X tiene:
- un miembro de variante con un constructor correspondiente no trivial y X es una clase de tipo union,
- un miembro de datos no estáticos del tipo de clase M (o matriz del mismo) que no se puede copiar / mover porque la resolución de sobrecarga (13.3), aplicada al constructor correspondiente de M, da como resultado una ambigüedad o una función que se elimina o inaccesible del constructor incumplido,
- una clase base directa o virtual B que no se puede copiar / mover porque la resolución de sobrecarga (13.3), como se aplica al constructor correspondiente de B, da como resultado una ambigüedad o una función que se elimina o inaccesible del constructor predeterminado,
- cualquier clase base directa o virtual o miembro de datos no estáticos de un tipo con un destructor que se elimine o inaccesible del constructor predeterminado,
- para el constructor de copia, un miembro de datos no estático del tipo de referencia rvalue, o
- para el constructor de movimientos, un miembro de datos no estático o una clase base directa o virtual con un tipo que no tiene un constructor de movimiento y no se puede copiar de manera trivial.
VS2010 no lo hace porque no eran estándar en el momento de la implementación.