Почему 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";потребовалась бы диагностика, так как это неправильно
Натан Оливер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.