Dlaczego std :: swap nie działa na elementach wektora <bool> w Clang / Win?

14

Mam taki kod:

#include <vector>
#include <utility>

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

vector<bool>Pomijając argumenty na temat rozsądku , działało to dobrze:

  • Clang dla komputerów Mac
  • Visual Studio dla Windows
  • GCC dla systemu Linux

Następnie próbowałem zbudować go za pomocą Clanga w systemie Windows i otrzymałem następujący błąd (skrócony):

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>&&

Dziwi mnie, że wyniki różnią się w zależności od implementacji.

Dlaczego nie działa z Clangiem w systemie Windows?

Lekkość Wyścigi na orbicie
źródło
Sądzę więc, że potrzebne jest wyjaśnienie: Czy jest to wynik wartości operator[]? i może std::swapdziałać na wartościach i wartościach?
Mgetz
@Mgetz Tak. Nie. W tej kolejności. To pytanie zostało zadane „na poważnie” na osobności i pomyślałem, że wystarczająco zabawne jest to, że odpowiedź brzmi: „Clang / Win nie jest zepsuty; kod został złamany przez cały czas, ale kombinacje głównego łańcucha narzędzi nigdy nie zadały sobie trudu, aby ci powiedzieć „aby to zapisać tutaj: P
Wyścigi lekkości na orbicie
2
Podobnie jak w przypadku FYI, nie kompiluje się w VS 2019 z /permissive-(zgodnością), co i tak powinno być ogólnie używane;)
ChrisMM
1
@ChrisMM Rzeczywiście! Wyłączony tryb zgodności był częścią układanki. (Chociaż nie wiedzieliśmy o tym, zanim się temu przyjrzeliśmy!) I moja odpowiedź wskazuje, że: P
Wyścigi lekkości na orbicie

Odpowiedzi:

15

Standard nie wymaga tego do kompilacji na żadnym zestawie narzędzi!

Po pierwsze, vector<bool>to dziwne, a indeksowanie daje tymczasowy obiekt typu proxy std::vector<bool>::reference, a nie rzeczywisty bool&.

Komunikat o błędzie mówi ci, że nie może powiązać tego tymczasowego z constodwołaniem innym niż wartość w ogólnej template <typename T> std::swap(T& lhs, T& rhs)implementacji.

Rozszerzenia!

Jednak okazuje się, że libstdc ++ definiuje przeciążenie dla std::swap(std::vector<bool>::reference, std::vector<bool>::reference), ale ten jest rozszerzeniem standardu (lub, jeśli jest tam, nie mogę znaleźć żadnych dowodów na to).

Libc ++ też to robi .

Domyślam się, że implementacja stdlib programu Visual Studio, której nadal używasz, nie robi tego , ale aby dodać obrażenia do obrażeń , możesz powiązać tymczasowe odniesienia do wartości z VS (chyba że używasz trybu zgodności), więc standardowa, „ogólna” std::swapfunkcja działa do momentu zastąpienia kompilatora VS bardziej restrykcyjnym kompilatorem Clanga.

W rezultacie polegałeś na rozszerzeniach we wszystkich trzech łańcuchach narzędzi, dla których działał on dla Ciebie, a kombinacja Clang na Windows jest jedyną faktycznie wykazującą ścisłą zgodność.

(Moim zdaniem te trzy łańcuchy narzędzi powinny to zdiagnozować, więc cały czas nie wysyłałeś nieprzenośnego kodu.)

Co teraz?

Dodanie własnej specjalizacji std::swapi może być kuszące std::vector<bool>::reference, ale nie można tego robić w przypadku standardowych typów; w rzeczywistości byłoby to sprzeczne z przeciążeniami, które libstdc ++ i libc ++ zdecydowały się dodać jako rozszerzenia.

Aby być przenośnym i zgodnym, należy zmienić kod .

Być może dobry, staromodny:

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

Lub skorzystaj ze specjalnej statycznej funkcji składowej, która robi dokładnie to, co chciałeś :

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

Również pisownia w następujący sposób:

vb.swap(vb[0], vb[1]);
Lekkość Wyścigi na orbicie
źródło
Jeśli chodzi o, ale nie powinno to AFAIK, mogą to zrobić. Dopóki nie złamią zgodnego kodu, mogą rozszerzyć implementację, aby złamany kod był „OK”.
NathanOliver,
@ NathanOliver-ReinstateMonica Cóż, dobrze. Czy nie muszą jednak zdiagnozować przynajmniej użycia takich rzeczy? eel.is/c++draft/intro.compliance#8
Lekkość wyścigów na orbicie
@LightnessRaceswithMonica czy jest jakiś język, który zabrania tego rozszerzenia?
Mgetz
@Mgetz Przepraszam, nie jestem biegły we wszystkich istniejących językach, więc nie mogę na to odpowiedzieć
Lightness Races in Orbit
Nie jestem pewien, czy stosuje takie rozszerzenia, które są źle sformułowane zgodnie z tym dokumentem . Dodali przeciążenie, które zajmujestd::vector<bool>::reference nic nie jest źle sformułowane. Wydaje mi się, że użycie czegoś takiego char * foo = "bar";wymagałoby diagnostyki, ponieważ jest to źle sformułowane.
NathanOliver,