Dlaczego nie ma domyślnego move-assignment/move-constructor?
Jestem prostym programistą. Zmienne członków mojej klasy najczęściej składają się z POD-typów i STL-kontenerów. Z tego powodu rzadko muszę pisać operatory przypisań lub konstruktory kopiujące, ponieważ są one domyślnie zaimplementowane.
Dodaj do tego, Jeśli używam std::move
na obiektach nie ruchomych, to wykorzystuje operator przypisania, co oznacza, że {[0] } jest całkowicie bezpieczny.
this->member1_ = std::move(other.member1_);...
"
Ale tak nie jest (przynajmniej nie w Visual 2010), Czy Jest jakiś szczególny powód?
[[3]} ważniejsze; [12]}czy jest jakiś sposób, aby to obejść?Update: Jeśli spojrzysz na odpowiedź Gmannickga, zapewnia on świetne makro do tego. A jeśli nie wiesz, jeśli zaimplementujesz semantykę move-semantics, możesz usunąć swap member funkcja.
4 answers
Domyślne generowanie konstruktorów ruchu i operatorów przydziałów było sporne i w ostatnich wersjach standardu C++ wprowadzono poważne zmiany, więc obecnie dostępne Kompilatory będą prawdopodobnie zachowywać się inaczej w odniesieniu do domyślnego generowania.
[[5]} aby dowiedzieć się więcej o historii wydania, zobacz listę artykułów WG21 z 2010 roku i wyszukaj "mov"Obecna Specyfikacja (N3225, od listopada) stwierdza (N3225 12.8/8):
Jeśli definicja klasy
X
nie deklaruje jawnie konstruktora move, zostanie ona domyślnie zadeklarowana jako domyślna wtedy i tylko wtedy, gdy
X
nie posiada deklarowanego przez użytkownika konstruktora kopiującego oraz
X
nie posiada zadeklarowanego przez użytkownika operatora przypisywania kopii,
X
nie posiada zadeklarowanego przez użytkownika operatora przypisania ruchu,
X
nie posiada destruktora zadeklarowanego przez użytkownika orazKonstruktor move nie byłby domyślnie zdefiniowany jako usunięty.
Istnieje podobny język w 12.8/22 określający, kiedy operator przypisania move jest domyślnie zadeklarowany jako domyślny. Pełną listę zmian wprowadzonych w celu obsługi aktualnej specyfikacji generowania ruchów niejawnych można znaleźć w N3203: zaostrzenie warunków generowania ruchów niejawnych , która w dużej mierze opierała się na jednej z rezolucji zaproponowanych przez Bjarne Tekst stroustrupa N3201: Moving right along .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-22 06:12:55
Domyślnie generowane konstruktory ruchu były brane pod uwagę dla standardu, ale mogą być niebezpieczne. Zobacz analizę Dave ' a Abrahamsa .
W końcu jednak standard zawierał Ukryte generowanie konstruktorów ruchu i operatorów przydziału ruchu, choć z dość dużą listą ograniczeń:
Jeśli definicja klasy X nie deklaruje jawnie konstruktora move, zostanie ona domyślnie zadeklarowana jako defaulted wtedy i tylko wtedy, gdy
- X nie posiada deklarowanego przez użytkownika konstruktora kopiującego,
- X nie posiada zadeklarowanego przez użytkownika operatora przypisywania kopii,
- X nie posiada zadeklarowanego przez użytkownika operatora przypisania ruchu,
- X nie posiada destruktora zadeklarowanego przez użytkownika i
- konstruktor move nie będzie domyślnie zdefiniowany jako usunięty.
To jednak nie wszystko. Ctor może być zadeklarowany, ale nadal zdefiniowany jako usunięty:
Domyślnie zadeklarowana Kopia/ruch constructor jest inline publicznym członkiem swojej klasy. Domyślny konstruktor kopiowania/przenoszenia dla klasy X jest zdefiniowany jako usunięty (8.4.3), jeśli X ma:
- element wariantowy z nietrywialnym odpowiednikiem konstruktora i X jest klasą podobną do Unii,
- niestatyczny element danych klasy typu M (lub ich tablica), który nie może być kopiowany / przenoszony, ponieważ rozdzielczość przeciążenia (13.3), zastosowana do odpowiedniego konstruktora m, powoduje niejednoznaczność lub funkcję, która jest usuwana lub niedostępna od domyślnego konstruktora,
- bezpośrednia lub wirtualna klasa bazowa B, której nie można skopiować/przenieść, ponieważ rozdzielczość przeciążenia (13.3), zastosowana do odpowiedniego konstruktora B, powoduje niejednoznaczność lub funkcję, która jest usuwana lub niedostępna z domyślnego konstruktora,
- Dowolna bezpośrednia lub wirtualna klasa bazowa lub niestatyczny element danych typu z destruktorem, który jest usuwany lub Niedostępny z domyślnego konstruktora,
- dla konstruktora kopiującego dane niestatyczne członek typu referencyjnego rvalue, lub
- dla konstruktora move, niestatyczny element danych lub bezpośrednia lub wirtualna klasa bazowa z typem, który nie ma konstruktora move i nie jest trywialnie kopiowalny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-28 19:02:01
(Jak na razie pracuję nad głupim makrem...)Tak, ja też. Oto twoje makro:
// 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
(usunąłem prawdziwe komentarze, które są długie i dokumentalne.)
Określasz bazy i/lub członków w swojej klasie jako listę preprocesorów, na przykład:
#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;
};
I wychodzi konstruktor ruchu i operator przypisania ruchu.
(na marginesie, jeśli ktoś wie, jak Mogę połączyć szczegóły w jeden makro, byłoby super.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-28 08:39:32
VS2010 tego nie robi, ponieważ nie były standardowe w momencie implementacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-27 18:25:40