Warum funktioniert std :: swap nicht mit Vektorelementen <bool> unter Clang / Win?

14

Ich habe folgenden Code:

#include <vector>
#include <utility>

int main()
{
   std::vector<bool> vb{true, false};
   std::swap(vb[0], vb[1]);
}

Argumente über die Vernunft vector<bool>beiseite, das funktionierte gut bei:

  • Clang für Mac
  • Visual Studio für Windows
  • GCC für Linux

Dann habe ich versucht, es mit Clang unter Windows zu erstellen und habe den folgenden Fehler (gekürzt) erhalten:

error: no matching function for call to 'swap'
                                std::swap(vb[0], vb[1]);
                                ^~~~~~~~~

note: candidate function [with _Ty = std::_Vb_reference<std::_Wrap_alloc<std::allocator<unsigned int> > >, $1 = void] not viable: expects an l-value for 1st argument
inline void swap(_Ty& _Left, _Ty& _Right) _NOEXCEPT_COND(is_nothrow_move_constructible_v<_Ty>&&

Ich bin überrascht, dass die Ergebnisse je nach Implementierung unterschiedlich sind.

Warum funktioniert es nicht mit Clang unter Windows?

Leichtigkeitsrennen im Orbit
quelle
Ich denke, die Klarstellung ist: Ist das Ergebnis operator[]eines Wertes? und kann std::swapmit rWerten und xWerten arbeiten?
Mgetz
@Mgetz Ja. In dieser Reihenfolge. Diese Frage wurde neulich privat "echt" gestellt, und ich fand es ausreichend unterhaltsam, dass die Antwort "Clang / Win ist nicht kaputt; der Code war die ganze Zeit kaputt, aber die Mainstream-Toolchain-Combos haben sich nie die Mühe gemacht, es Ihnen zu sagen." "um es hier aufzuschreiben: P
Lightness Races in Orbit
2
Genau wie /permissive-
Information
1
@ ChrisMM In der Tat! Der Konformitätsmodus aus war ein Teil des Puzzles. (Obwohl wir das nicht wussten, bevor wir uns damit befassten!) Und meine Antwort weist darauf hin: P
Leichtigkeitsrennen im Orbit

Antworten:

15

Der Standard verlangt dies nicht, um in irgendeiner Toolchain zu kompilieren !

Wenn Sie sich zunächst daran erinnern, dass dies vector<bool>seltsam ist und wenn Sie es abonnieren, erhalten Sie ein temporäres Objekt eines Proxy-Typs namens std::vector<bool>::referenceund kein tatsächliches bool&.

Die Fehlermeldung weist darauf hin, dass diese temporäre constDatei nicht an eine nicht wertvolle Referenz in der generischen template <typename T> std::swap(T& lhs, T& rhs)Implementierung gebunden werden kann .

Erweiterungen!

Es stellt sich jedoch heraus, dass libstdc ++ eine Überladung für definiertstd::swap(std::vector<bool>::reference, std::vector<bool>::reference) , dies ist jedoch eine Erweiterung des Standards (oder, falls vorhanden, kann ich keine Beweise dafür finden).

libc ++ macht das auch .

Ich würde vermuten, dass die Visual Studio stdlib-Implementierung, die Sie noch verwenden, dies nicht tut , aber um die Verletzung zusätzlich zu beleidigen, können Sie temporäre Werte an l-Wert-Referenzen in VS binden (es sei denn, Sie verwenden den Konformitätsmodus) Die Standardfunktion "generic" std::swapfunktioniert so lange, bis Sie den VS-Compiler durch den strengeren Clang-Compiler ersetzen.

Infolgedessen haben Sie sich bei allen drei Toolchains, für die es für Sie funktioniert hat, auf Erweiterungen verlassen, und die Clang on Windows-Kombination ist die einzige, die tatsächlich strikte Konformität aufweist.

(Meiner Meinung nach hätten diese drei Toolchains dies diagnostizieren sollen, damit Sie nicht die ganze Zeit nicht portierbaren Code ausgeliefert haben. 😊)

Was jetzt?

Es mag verlockend sein, eine eigene Spezialisierung von std::swapund hinzuzufügen std::vector<bool>::reference, aber Sie dürfen dies nicht für Standardtypen tun. In der Tat würde dies im Widerspruch zu den Überladungen stehen, die libstdc ++ und libc ++ als Erweiterungen hinzugefügt haben.

Um portabel und kompatibel zu sein, sollten Sie Ihren Code ändern .

Vielleicht ein guter altmodischer:

const bool temp = vb[0];
vb[0] = vb[1];
vb[1] = temp;

Oder nutzen Sie die spezielle statische Elementfunktion, die genau das tut, was Sie wollten :

std::vector<bool>::swap(vb[0], vb[1]);

Auch wie folgt buchstabierbar:

vb.swap(vb[0], vb[1]);
Leichtigkeitsrennen im Orbit
quelle
In Bezug auf die, aber es soll nicht AFAIK, dürfen sie dies tun. Solange sie keinen konformen Code beschädigen, können sie die Implementierung erweitern, um fehlerhaften Code "OK" zu machen.
NathanOliver
@ NathanOliver-ReinstateMonica Na gut. Müssen sie nicht zumindest die Verwendung solcher Dinge diagnostizieren? eel.is/c++draft/intro.compliance#8
Leichtigkeitsrennen im Orbit
@LightnessRaceswithMonica Gibt es eine Sprache, die diese Erweiterung verbietet?
Mgetz
@Mgetz Entschuldigung, ich bin nicht in allen vorhandenen Sprachen vertraut, daher kann ich das nicht beantworten
Lightness Races in Orbit
Ich bin mir nicht sicher, ob solche Erweiterungen verwendet werden, die gemäß diesem Dokument schlecht geformt sind . Sie fügten eine Überlastung hinzu, die eine nimmt, std::vector<bool>::referenceso dass nichts wirklich schlecht geformt ist. Für mich klingt es so, als char * foo = "bar";würde die Verwendung von so etwas wie eine Diagnose erfordern, da diese schlecht geformt ist.
NathanOliver