Mam std::vector
obiekty określonej klasy A
. Klasa jest nietrywialna i ma zdefiniowane konstruktory kopiujące i przenoszące.
std::vector<A> myvec;
Jeśli wypełnię wektor A
obiektami (używając np. myvec.push_back(a)
), Wektor będzie się powiększał, używając konstruktora kopiującego A( const A&)
do tworzenia nowych kopii elementów w wektorze.
Czy mogę w jakiś sposób wymusić, że A
zamiast tego używany jest konstruktor przenoszenia klasy ?
Odpowiedzi:
Musisz poinformować C ++ (konkretnie
std::vector
), że twój konstruktor przenoszenia i destruktor nie rzucają, używającnoexcept
. Następnie konstruktor przenoszenia zostanie wywołany, gdy wektor wzrośnie.Oto jak zadeklarować i zaimplementować konstruktor ruchu, który jest przestrzegany przez
std::vector
:A(A && rhs) noexcept { std::cout << "i am the move constr" <<std::endl; ... some code doing the move ... m_value=std::move(rhs.m_value) ; // etc... }
Jeśli konstruktor nie jest
noexcept
,std::vector
nie można go używać, ponieważ wtedy nie będzie w stanie zapewnić gwarancje wyjątków wymaganych przez normy.Aby uzyskać więcej informacji na temat tego, co zostało powiedziane w standardzie, przeczytaj semantykę ruchu i wyjątki w języku C ++
Podziękowania dla Bo, który zasugerował, że może to mieć związek z wyjątkami. Weź również pod uwagę rady Kerreka SB i użyj ich,
emplace_back
jeśli to możliwe. To może być szybsze (ale często nie jest), to może być jaśniejszy i bardziej zwarta, ale jest też kilka pułapek (zwłaszcza bez wyraźnych konstruktorów).Edytuj , często domyślnym jest to, czego chcesz: przenieś wszystko, co można przenieść, skopiuj resztę. Aby wyraźnie o to poprosić, napisz
A(A && rhs) = default;
Robiąc to, otrzymasz noexcept, jeśli to możliwe: Czy domyślny konstruktor Move jest zdefiniowany jako noexcept?
Należy pamiętać, że wczesne wersje programu Visual Studio 2015 i starsze nie obsługiwały tego, mimo że obsługuje semantykę przenoszenia.
źródło
value_type
„y ruch konstruktor jestnoexcept
? Być może język ogranicza zestaw kandydatów wywołania funkcji, gdy zakres wywołania jest równieżnoexcept
funkcją?noexcept
konstruktor ruchu.is_nothrow_move_constructible
będzie prawdziwe, jeśli istniejenothrow
konstruktor kopiujący. Nie znam żadnego prawdziwego przypadku drogichnothrow
konstruktorów kopiujących, więc nie jest jasne, czy to naprawdę ma znaczenie.noexcept
zarówno w nagłówku, jak i implementacji, a kiedy wykonuję push_back (std :; move), nadal wywołuje konstruktor kopiujący. Rwę sobie tutaj włosy.std::move()
niewłaściwegopush_back()
połączenia. Jeden z tych momentów, kiedy tak bardzo szukasz problemu, że nie widzisz przed sobą oczywistego błędu. A potem była pora obiadowa i zapomniałem usunąć swój komentarz.Co ciekawe, wektor gcc 4.7.2 używa konstruktora przenoszenia tylko wtedy, gdy zarówno konstruktor przenoszenia, jak i destruktor są
noexcept
. Prosty przykład:struct foo { foo() {} foo( const foo & ) noexcept { std::cout << "copy\n"; } foo( foo && ) noexcept { std::cout << "move\n"; } ~foo() noexcept {} }; int main() { std::vector< foo > v; for ( int i = 0; i < 3; ++i ) v.emplace_back(); }
To daje oczekiwane:
Jednak kiedy usuwam
noexcept
z~foo()
, wynik jest inny:Myślę, że to również odpowiada na to pytanie .
źródło
Wydaje się, że jedynym sposobem (dla C ++ 17 i wczesnych), aby wymusić
std::vector
użycie semantyki przenoszenia przy realokacji, jest usunięcie konstruktora kopiującego :). W ten sposób użyje twoich konstruktorów przenoszenia lub umrze próbując w czasie kompilacji :).Istnieje wiele reguł, w których
std::vector
NIE WOLNO używać konstruktora przenoszenia przy ponownej alokacji, ale nic o tym, gdzie MUSI go UŻYWAĆ .template<class T> class move_only : public T{ public: move_only(){} move_only(const move_only&) = delete; move_only(move_only&&) noexcept {}; ~move_only() noexcept {}; using T::T; };
Relacja na żywo
lub
template<class T> struct move_only{ T value; template<class Arg, class ...Args, typename = std::enable_if_t< !std::is_same_v<move_only<T>&&, Arg > && !std::is_same_v<const move_only<T>&, Arg > >> move_only(Arg&& arg, Args&&... args) :value(std::forward<Arg>(arg), std::forward<Args>(args)...) {} move_only(){} move_only(const move_only&) = delete; move_only(move_only&& other) noexcept : value(std::move(other.value)) {}; ~move_only() noexcept {}; };
Kod na żywo
Twoja
T
klasa musi miećnoexcept
konstruktor przenoszenia / operator przypisania inoexcept
destruktor. W przeciwnym razie pojawi się błąd kompilacji.std::vector<move_only<MyClass>> vec;
źródło