У меня есть такой код:
#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?/permissive-
(соответствием), которое обычно должно использоваться в любом случае;)Ответы:
Стандарт не требует этого для компиляции на любом наборе инструментов!
Сначала вспомните, что
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 ++ решили добавить в качестве расширений.Таким образом, чтобы быть переносимым и совместимым, вы должны изменить свой код .
Возможно, хороший старомодный
Или используйте специальную статическую функцию-член, которая делает именно то, что вы хотели :
Также пишется следующим образом:
источник
std::vector<bool>::reference
так что на самом деле ничто не является плохо сформированным. Для меня это звучит так, как будто для использования чего-то подобногоchar * foo = "bar";
потребовалась бы диагностика, так как это неправильно