Почему std :: swap не работает с элементами вектора <bool>в Clang / Win? - PullRequest
14 голосов
/ 01 ноября 2019

У меня есть код, подобный этому:

#include <vector>
#include <utility>

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

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

  • Clang для 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?

1 Ответ

15 голосов
/ 01 ноября 2019

Стандарт не требует, чтобы это компилировалось на любом наборе инструментов!

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

Сообщение об ошибке сообщает, что оно не может привязать это временное значение к ссылке не-1010 * lvalue в общей реализации 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 на stricter Clang compiler.

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

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

Что теперь?

Может быть заманчиво добавить свою собственную специализацию 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]);
...