Fehler C2280 move-ctor deleted und std::push_back
-
@Finnegan sagte in Fehler C2280 move-ctor deleted und std::push_back:
Und noch was wichtiges: Der Move-Konstruktor für deinen Typ ist
Slice(Slice&& scr);
nicht
const Slice&& scr
. Versuch das mal zu ändern und schau ob das eventuell funktioniert.Ändert nichts am verhalten beim MSVC. Es kommt der gleiche fehler.
-
@firefly Nochwas relevantes bezüglich der Kandidaten für Overload Resolution:
Additional rules for constructor candidates
Defaulted move constructor and move assignment that are defined as deleted are never included in the list of candidate functions. (since C++11)
Mit dieser Regel liegen dann doch wieder GCC und Clang richtig und MSVC falsch. Mein kleines Testprogramm im vorherigen Beitrag müsste also eigentlich kompilieren - falls ich den Satz richtig gelesen und verstanden habe ... sind
T(T&&) = default
undoperator=(T&&) = delete
gemeint, oder dass die compiler-generierten defaults der beidendelete
d sind?Darf ich fragen welche Motivation hinter dem gelöschten Move-Konstruktor steckt? Ich hatte für so etwas bisher noch keinen Bedarf und vielleicht kann man das ja auch anders lösen, so dass es zumindest auch mit MSVC funktioniert.
-
@Finnegan Öhm ich bin nicht der thread ersteller Hatte das jetzt nur auf die schnelle via godbolt.org getestet
-
@firefly sagte in Fehler C2280 move-ctor deleted und std::push_back:
@Finnegan Öhm ich bin nicht der thread ersteller Hatte das jetzt nur auf die schnelle via godbolt.org getestet
Oh sorry, stimmt ja..dachte ich echt grad wegen der Antwort. Ich meine natürlich @SoIntMan
-
@Finnegan sagte in Fehler C2280 move-ctor deleted und std::push_back:
Darf ich fragen welche Motivation hinter dem gelöschten Move-Konstruktor steckt? Ich hatte für so etwas bisher noch keinen Bedarf und vielleicht kann man das ja auch anders lösen, so dass es zumindest auch mit MSVC funktioniert.
ich hab das "per zufall" rausgefunden, und habe mich gefragt was la lass ist. ich verwende dann auch den move ctor.
@DocShoe sagte in Fehler C2280 move-ctor deleted und std::push_back:
@SoIntMan
Verstehe ich auch nicht, aber kannst du y const machen? Damit dürfte y nicht mehr moevable sein.das hat auch nicht funktioniert.
Danke euch, ist aber echt suspekt
-
Ja ist echt suspekt. Auf godbolt.org kann man bis zu vs2017 zurück gehen und auch dort tritt die Fehlermeldung auf.
Daher ist dieses verhalten in der STL des C++ Compilers von MS schon länger drinn.
Eventuell haben wir hier es mit einer Definitionslücke im Standard zu tun, wodurch es zu unterschiedlichen Interpretationen kommt.
Aber ob meine Spekulation stimmt kann ich nicht sagen, da ich mich mit dem C++ Standard und dessen interpretation überhaupt nicht auskenne.Wobei ich aktuell eher dazu tendieren würde, dass die Interpretation von GCC und Clang die korrekte ist und die von MSVC als die falsche.
Wenn ich mich recht entsinne, ist es nicht so, wenn für eine Klasse der copy constructor explizit definiert wird, dass dadurch der move constructor und move assignment operator implizit als nicht vorhanden definiert wird?
Wodurch eigentlich nie die move semantic verwendet werden sollte.
-
@firefly sagte in Fehler C2280 move-ctor deleted und std::push_back:
Ja ist echt suspekt. Auf godbolt.org kann man bis zu vs2017 zurück gehen und auch dort tritt die Fehlermeldung auf.
Daher ist dieses verhalten in der STL des C++ Compilers von MS schon länger drinn.
Eventuell haben wir hier es mit einer Definitionslücke im Standard zu tun, wodurch es zu unterschiedlichen Interpretationen kommt.
Aber ob meine Spekulation stimmt kann ich nicht sagen, da ich mich mit dem C++ Standard und dessen interpretation überhaupt nicht auskenne.
Wobei ich aktuell eher dazu tendieren würde, dass die Interpretation von GCC und Clang die korrekte ist und die von MSVC als die falsche.
Wenn ich mich recht entsinne, ist es nicht so, wenn für eine Klasse der copy constructor explizit definiert wird, dass dadurch der move constructor und move assignment operator implizit als nicht vorhanden definiert wird?
Wodurch eigentlich nie die move semantic verwendet werden sollte.hmm das bedeutet ja dass hier noch nie jemand über das "Problem" gestolpert ist?:)
EDIT: Danke für den "godbold.org" linke, sehr hilfreich;()
-
Zumindest auf stackoverflow gibt es dazu was: https://stackoverflow.com/questions/20819192/attempt-to-push-back-a-lvalue-with-move-ctor-deleted-doesnt-compile
-
@SoIntMan sagte in Fehler C2280 move-ctor deleted und std::push_back:
EDIT: Danke für den "godbold.org" linke, sehr hilfreich;()
Die URL ist https://godbolt.org/ ... "godbold" gibt es nicht. Und ja, sehr angenehm/nützlich die Seite.
-
Interessanter Post von Howard Hinnant dort:
= delete
bedeutet gerade nicht, daß diese Überladung nicht herangezogen wird, sondern explizit, daß man dort einen Kompilerfehler erzwingen will, sobald ein passender Parameter benutzt wird.Also einfach nur die anderen Überladungen anbieten (und damit ist der Move-Konstruktor automatisch nicht benutzbar).
-
@Th69 sagte in Fehler C2280 move-ctor deleted und std::push_back:
Interessanter Post von Howard Hinnant dort:
= delete
bedeutet gerade nicht, daß diese Überladung nicht herangezogen wird, sondern explizit, daß man dort einen Kompilerfehler erzwingen will, sobald ein passender Parameter benutzt wird.Also einfach nur die anderen Überladungen anbieten (und damit ist der Move-Konstruktor automatisch nicht benutzbar).
Das ist korrekt, zu mindestens mit gcc 12, liefert folgender Beispielcode, dass immer copy constructor/assignment operator aufgerufen wird
#include <iostream> #include <vector> class Slice { public: Slice(const size_t& id, const size_t& maxSize) {std::cout<<"ctor\n";} Slice(const Slice& src){std::cout<<"copy ctor\n";} Slice& operator=(const Slice &src){std::cout<<"copy assignment\n"; return *this;} virtual ~Slice() {} private: }; int main() { std::vector<Slice> list; std::cout<<"begin x\n"; Slice x(0,0); std::cout<<"begin y\n"; Slice y(x); std::cout<<"begin y2\n"; Slice y2 = std::move(x); std::cout<<"begin y2 assign rvalue\n"; y2 = std::move(y); std::cout<<"begin push_back\n"; list.push_back(y2); std::cout<<"end\n"; return 0; }
Ausgabe:
begin x ctor begin y copy ctor begin y2 copy ctor begin y2 assign rvalue copy assignment begin push_back copy ctor end
@SoIntMan: Du musst nur den copy assignment operator (
Slice& operator=(const Slice &src)
) definieren und implementieren. Dadurch ist jede move semantic automatisch deaktiviert.unabhängig von dem Problem solltest du unbedingt den copy assignment operator implementieren, da du einen eigenen copy constructor definiert hast.
Ansonsten wird einer vom compiler erstellt, der sehr wahrscheinlich nicht das macht was du erwartest (weil sonst hättest du auch kein eigenen copy constructor implementiert)
-
@firefly sagte in Fehler C2280 move-ctor deleted und std::push_back:
@SoIntMan: Du musst nur den copy assignment operator (Slice& operator=(const Slice &src)) definieren und implementieren. Dadurch ist jede move semantic automatisch deaktiviert.
super, sehr schön.. danke für euren support
-
@firefly sagte in Fehler C2280 move-ctor deleted und std::push_back:
Laut der kompletten fehlermeldung wird std::vector::push_back(const T&) verwendet.
Nur, wieso auch immer, meint der compiler oder die implementierung der STL dass intern ein codepath genutzt werden soll der ein rvalue reference erwartet.Der Teil passt schon so.
push_back(const T&)
muss verwendet werden, selbst wenn der Typ movable wäre, weil im Codey
steht und nichtstd::move(y)
.Der Code-Pfad der dann trotzdem den move-ctor referenziert ist die "grow" Implementierung. Dabei müssen ja alle Elemente vom alten Speicherblock in den neuen Speicherblock "verschoben" werden. Und für Typen die "noexcept moveable" sind wird da halt der move-ctor verwendet.
Der Fehler ist einfach dass MSVC der Meinung ist dass der Typ "noexcept moveable" ist, obwohl er einen gelöschten move-ctor hat.
-
@hustbaer sagte in Fehler C2280 move-ctor deleted und std::push_back:
Der Fehler ist einfach dass MSVC der Meinung ist dass der Typ "noexcept moveable" ist, obwohl er einen gelöschten move-ctor hat.
Der aber nur ausgelöst wird, wenn explizit z.b. der move constructor (wie im falle von SoIntMan) als deleted markiert ist.
Wobei laut folgenden hinweis aus https://stackoverflow.com/questions/20819192/attempt-to-push-back-a-lvalue-with-move-ctor-deleted-doesnt-compile
das angeblich kein "Bug" sei und vom standard so gefordert sei:@IgorTandetnik: This is not a bug. As per private communications with Stephan Lavavej, 23.2.1/13 from N3797 states: — T is CopyInsertable into X means that, in addition to T being MoveInsertable into X [...] "in addition to" seems to imply CI requires MI? – ForeverLearning
Und Laut diesem Abschnitt der Antwort von Howard Hinnant gilt wohl folgendes:
Deleted move members attract rvalue arguments, and if they bind, result in a compile-time error. Non-existent move members do not attract any arguments at all. Therefore other overloads, such as copy members, are available to be chosen by overload resolution. rvalues in particular will get (often confusingly) snagged by deleted move members. But with nonexistent move members, rvalues are free to bind to (traditional const&) copy members.
Wenn ich das richtig verstehe, dann wird ein member, welcher explizit als deleted markiert ist, in der suche nach der passenden Methode mit einbezogen.
Im Falle des move constructors wird diese dann ausgewählt, wenn ein rvalue argument übergeben werden soll an einen constructor.
Da die Methode aber explizit als deleted markiert ist, löst das ein compiler fehler aus.Die Frage ist ob das so im standard definiert ist oder nicht.
Da GCC/Clang auch explizit deleted methoden aussortieren aber der MSVC nicht (und das seit mindestens der Version von VS 2017) scheint so eine definition nicht zu geben im Standard, die das explizit fordert.
-
@firefly
Ah, mein Fehler.
std::is_nothrow_move_constructible_v
von solchen Typen ist mit MSVC eh richtigerweisefalse
.Was den Rest angeht: keine Ahnung ob da Clang und GCC oder MSVC Recht haben.
-
@hustbaer sagte in Fehler C2280 move-ctor deleted und std::push_back:
Was den Rest angeht: keine Ahnung ob da Clang und GCC oder MSVC Recht haben.
Ich glaube ich habe mein simples Beispiel von oben fehlinterpretiert. GCC und Clang default-konstuieren in Zeile 10 mit
A a{ A{} };
das Objekt direkt ina
, ohne dass Move und/oder Copy-Konstruktoren berücksichtigt werden und daher auch kein Fehler gemeldet wird.Wenn man das so umformuliert:
A a0; A a{ std::move(a0) };
dann laufen auch GCC und Clang auf einen Fehler.
Der eigentliche Unterschied ist also nicht, dass GCC und Clang in diesem Fall den Copy-Konstruktor verwenden, sondern dass sie eben nur den Default-Konstruktor aufrufen. Ich denke das gehört in diesem Fall sogar zur Mandatory Copy Elision, die vorhandene Copy/Move-Konstruktoren nicht berücksichtigt, womit MSVC in diesem Fall zu übereifrig wäre und hier falsch liegt.
Dass es überhaupt zu einem Fehler kommt finde ich schon etwas unintuitiv, da ja eben noch der Copy-Konstruktor existiert. Aber eine
delete
d-Funktion ist eben "deklariert" und wird tatsächlich für Overload Resolution herangezogen.Zuerst dachte ich, da gäbe es mit dem hier auf cppreference sei die entsprechende Ausnahme:
Additional rules for constructor candidates
Defaulted move constructor and move assignment that are defined as deleted are never included in the list of candidate functions. (since C++11)Die Formulierung fand ich aber recht seltsam. "Defaulted move" die als "deleted" definiert sind. Hä? Entweder "defaulted" oder "deleted", wie soll denn beides gehen?
Die relevante Stelle im Standard (in dieser Version unter 12.4.1 (8)) - verewendet diese ähnliche Formulierung:
A defaulted move special function that is defined as deleted is excluded from the set of candidate functions in all contexts.
und bringt aber auch ein Beispiel, was damit anscheinend gemeint ist:
struct A { A(); // #1 A(A &&); // #2 template<typename T> A(T &&); // #3 }; struct B : A { using A::A; B(const B &); // #4 B(B &&) = default; // #5, implicitly deleted struct X { X(X &&) = delete; } x; }; extern B b1; B b2 = static_cast<B&&>(b1); // calls #4: #1 is not viable, #2, #3, and #5 are not candidates struct C { operator B&&(); }; B b3 = C(); // calls #4
#5 ist so wie ich das verstehe ein
default
Move-Konstruktor der implizitdelete
d ist, da die Klasse einen Member mit einem explizitdelete
d Move-Konstruktor hat. Also "defaulted" und "deleted" gleichzeitig scheint es tatsächlich zu gebenDiese Ausnahme würde tatsächlich eine Workaround erlauben, wenn man es denn wirklich darauf anlegen will, unbedingt einen solchen gelöschten Move zu haben. Man könnte eben dafür sorgen, dass der "defaulted" Move-Konstruktor implizit "deleted" ist:
#include <iostream> #include <utility> struct A { A() = default; A(const A&) { std::cout << "copy A" << std::endl; } A(A&&) = default; struct X { X() = default; X(const X&) = default; X(X&&) = delete; } x; }; int main() { A a0; A a{ std::move(a0) }; }
Und in der Tat kompiliert das auf allen 3 Compilern und gibt jeweils
copy A
aus. Clang findet dieses barocke Konstrukt allerdings gar nicht toll und spuckt eine Warnung aus, man solleA(A&&)
doch bitte explizitdelete
n:
-
@Finnegan
In dem Beispiel kannst du dasA(A&&) = default;
auch genau so gut weglassen, da die Deklaration vonA(const A&)
schon dafür sorgt dassA
keinen move-ctor hat.
-
@hustbaer sagte in Fehler C2280 move-ctor deleted und std::push_back:
@Finnegan
In dem Beispiel kannst du dasA(A&&) = default;
auch genau so gut weglassen, da die Deklaration vonA(const A&)
schon dafür sorgt dassA
keinen move-ctor hat.Ich will ja für das Beispiel gerade einen Move Constructor haben, um ihn implizit löschen zu können. Das Ziel ist ja, den compiler-generierten Move-CTOR so zu löschen, dass er nicht für die Overload Resolution herangezogen wird (und dann wegen "deleted" auf einen Fehler läuft). Wenn erst gar keiner erzeugt wird, dann macht das ganze Beispiel keinen Sinn
Ferner ist
A(const A&)
eh nur für die Debug-Ausgabe um zu sehen, dass der Copy Constructor auch tatsächlich aufgerufen wird und eben nicht aus irgendeinem Grund dochA(A&&)
.Vielleicht ginge das auch noch etwas eleganter und ohne extra Speicher zu belegen mit ner Basisklasse, die ein gelöschtes Move hat (Empty Base Optimization)*.
Dennoch frage ich mich immer noch, welchen Use Case es für sowas geben könnte. Vielleicht ein Member, dessen Klasse ein sinnvolles Move implementiert, das man aber in speziell diesem Kontext (als Member einer bestimmten Klasse) unterbinden möchte (?).
* Edit: Ja, so ists besser, wenn man sowas wirklich ernsthaft nutzen will: https://godbolt.org/z/secahe6rs.
Die Warnungen stören in diesem Fall allerdings etwas, da das ja genau das ist, was erreicht werden soll - die gibts aber eh nur bei dem explizitendefault
.