Почему std :: swap не работает с векторными элементами <bool> в Clang / Win?

14

У меня есть такой код:

#include <vector>
#include <utility>

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

Аргументы о здравомыслии в vector<bool>стороне, это работало очень хорошо на:

  • Лязг для Mac
  • Visual Studio для Windows
  • GCC для Linux

Затем я попытался собрать его с помощью Clang в Windows и получил следующую ошибку (сокращенно):

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

Я удивлен, что результаты отличаются в разных реализациях.

Почему это не работает с Clang на Windows?

Гонки легкости на орбите
источник
Итак, я думаю, что необходимо уточнить: является ли результат operator[]lvalue? а может std::swapоперировать rvalues ​​и xvalues?
Mgetz
@Mgetz Да. Нет. В таком порядке. Этот вопрос был задан «по-настоящему» наедине на днях, и я подумал, что достаточно интересно, что ответ «Clang / Win не сломан; код был все время сломан, но основные комбинации инструментальных цепочек никогда не удосужились сказать вам ", чтобы написать это здесь: P
Гонки
2
Так же, как к вашему сведению, это не компилируется в VS 2019 с /permissive-(соответствием), которое обычно должно использоваться в любом случае;)
ChrisMM
1
@ ChrisMM Действительно! Отключение режима соответствия было частью головоломки. (Хотя мы не знали об этом, прежде чем разобраться в этом!) И мой ответ действительно указывает на это: P
Гонки

Ответы:

15

Стандарт не требует этого для компиляции на любом наборе инструментов!

Сначала вспомните, что vector<bool>это странно, и подписка дает временный объект с именем прокси std::vector<bool>::reference, а не фактический bool&.

Сообщение об ошибке говорит вам, что оно не может привязать это временное значение к constссылке без значения в универсальной template <typename T> std::swap(T& lhs, T& rhs)реализации.

Расширения!

Однако оказывается, что libstdc ++ определяет перегрузку для std::swap(std::vector<bool>::reference, std::vector<bool>::reference), но это расширение стандарта (или, если оно там, я не могу найти никаких доказательств этого).

libc ++ делает это тоже .

Я предполагаю, что реализация Visual Studio stdlib, которую вы все еще используете, не делает этого , но затем, чтобы добавить оскорбление травме, вы можете привязать временные ссылки к lvalue ссылкам в VS (если вы не используете режим соответствия), поэтому стандартная «универсальная» std::swapфункция работает до тех пор, пока вы не замените компилятор VS более строгим компилятором Clang.

В результате вы полагались на расширения для всех трех наборов инструментов, для которых он работал, и комбинация Clang on Windows - единственная, демонстрирующая строгое соответствие.

(По моему мнению, эти три набора инструментов должны были диагностировать это, чтобы вы не отправляли непереносимый код все это время. 😊)

Что теперь?

Может быть заманчиво добавить свою собственную специализацию std::swapи std::vector<bool>::reference, но вы не можете делать это для стандартных типов; действительно, это будет конфликтовать с перегрузками, которые libstdc ++ и libc ++ решили добавить в качестве расширений.

Таким образом, чтобы быть переносимым и совместимым, вы должны изменить свой код .

Возможно, хороший старомодный

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

Или используйте специальную статическую функцию-член, которая делает именно то, что вы хотели :

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

Также пишется следующим образом:

vb.swap(vb[0], vb[1]);
Гонки легкости на орбите
источник
Что касается, но это не должно AFAIK, им разрешено это делать. Пока они не нарушают соответствующий код, они могут расширить реализацию, чтобы сделать некорректный код "ОК".
Натан Оливер
@ NathanOliver-ReinstateMonica Хорошо, хорошо. Не должны ли они хотя бы диагностировать использование подобных вещей? eel.is/c++draft/intro.compliance#8
Гонки
@LightnessRaceswithMonica есть ли язык, который запрещает это расширение?
Mgetz
@Mgetz Извините, я не знаком со всеми существующими языками, поэтому я не могу ответить на это
Гонки
Я не уверен, применимы ли такие расширения, которые плохо сформированы в соответствии с этим документом . Они добавили перегрузку, std::vector<bool>::referenceтак что на самом деле ничто не является плохо сформированным. Для меня это звучит так, как будто для использования чего-то подобного char * foo = "bar";потребовалась бы диагностика, так как это неправильно
Натан Оливер