Jestem prostym programistą. Zmienne składowe mojej klasy najczęściej składają się z typów POD i kontenerów STL. Z tego powodu rzadko muszę pisać operatory przypisania lub konstruktory kopiujące, ponieważ są one implementowane domyślnie.
Dodajmy do tego, że jeśli używam std::move
na obiektach, które nie są ruchome, używa operatora przypisania, co oznacza, że std::move
jest całkowicie bezpieczny.
Ponieważ jestem prostym programistą, chciałbym skorzystać z możliwości przenoszenia bez dodawania konstruktora przenoszenia / operatora przypisania do każdej klasy, którą piszę, ponieważ kompilator mógłby po prostu zaimplementować je jako „ this->member1_ = std::move(other.member1_);...
”
Ale tak nie jest (przynajmniej nie w Visual 2010), czy jest jakiś szczególny powód?
Co ważniejsze; czy jest jakiś sposób, aby to obejść?
Aktualizacja: Jeśli spojrzysz na odpowiedź GManNickG, zapewni on świetne makro do tego. A jeśli nie wiesz, jeśli zaimplementujesz semantykę ruchu, możesz usunąć funkcję elementu członkowskiego wymiany.
źródło
MyClass::MyClass(Myclass &&) = default;
?Odpowiedzi:
Niejawne generowanie konstruktorów przenoszenia i operatorów przypisania było sporne, a ostatnie wersje C ++ standardu zostały poddane poważnym zmianom, więc obecnie dostępne kompilatory będą prawdopodobnie zachowywać się inaczej w odniesieniu do niejawnego generowania.
Aby uzyskać więcej informacji na temat historii wydania, zapoznaj się z listą artykułów WG21 z 2010 r. I wyszukaj „mov”
Aktualna specyfikacja (N3225, od listopada) podaje (N3225 12,8 / 8):
Jest podobny język w 12.8 / 22 określający, kiedy operator przypisania przeniesienia jest niejawnie zadeklarowany jako domyślny. Pełną listę zmian wprowadzonych w celu obsługi bieżącej specyfikacji generowania niejawnych ruchów można znaleźć w N3203: Zaostrzanie warunków generowania niejawnych ruchów , która była w dużej mierze oparta na jednej z rezolucji zaproponowanych w artykule Bjarne Stroustrupa N3201: Moving right along .
źródło
cannot be defaulted *in the class body*
. Tak więc zdefiniowałem destruktor na zewnątrz i zadziałało :). Jednak wydaje mi się to trochę dziwne. Czy ktoś ma wyjaśnienie? Kompilator to gcc 4.6.1virtual ~D() = default;
powinno to działać i nadal umożliwiać niejawny konstruktor ruchu.Niejawnie wygenerowane konstruktory przenoszenia zostały uwzględnione w standardzie, ale mogą być niebezpieczne. Zobacz analizę Dave'a Abrahamsa .
Ostatecznie jednak standard zawiera niejawne generowanie konstruktorów przenoszenia i operatorów przypisania przenoszenia, chociaż z dość dużą listą ograniczeń:
Jednak to nie wszystko w tej historii. Ctor można zadeklarować, ale nadal zdefiniować jako usunięty
źródło
Tweak2
. Przypuszczam, że ma to coś wspólnego z faktem, żeNumber
zostałby przeniesiony, avector
skopiowany ... ale nie jestem pewien: / Rozumiem, że problem spadłby kaskadowoTweak3
.Tak, ja też poszedłem tą drogą. 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
Wcześniejsze
// 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ą obszerne i dokumentalne.)
Określasz bazy i / lub składowe 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; };
Wychodzi konstruktor ruchu i operator przypisania ruchu.
(Na marginesie, jeśli ktoś wie, jak mogę połączyć szczegóły w jedno makro, byłoby to świetne.)
źródło
VS2010 nie robi tego, ponieważ nie były standardowe w momencie wdrożenia.
źródło