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?


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]);

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.