Чому 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[]значення? і може std::swapпрацювати на rvalues ​​і xvalues?
Mgetz

@Mgetz Так. Ні. У цьому порядку. На днях це запитання було задано "справжнім" приватно, і я вважав, що це досить цікаво, що відповідь була "Clang / Win не порушена; код був порушений весь цей час, але комбінації інструментальних ланцюгів інструментів ніколи не намагалися сказати вам" "написати це тут: P
Гонки легкості в орбіті

2
Як і FYI, це не компілюється у 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 ++ теж робить це .

Я здогадуюсь, що реалізація stdlib Visual Studio, яку ви все ще використовуєте, не робить , але потім, щоб додати образи до травми, ви можете прив’язати тимчасових користувачів до значення цінностей у VS (якщо ви не використовуєте режим відповідності), тож стандартна, "загальна" std::swapфункція працює, поки ви не заміните компілятор VS на більш жорсткий компілятор Clang.

Як результат, ви покладаєтесь на розширення на всіх трьох ланцюжках інструментів, для яких це працювало для вас, а комбінація Clang у 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

@ NathanOliver-ReinstateMonica Ну добре. Чи не потрібно їм хоча б діагностувати використання таких речей? eel.is/c++draft/intro.compliance#8
Гонки легкості в орбіті

@LightnessRaceswithMonica Чи існує мова, яка забороняє це розширення?
Mgetz

@Mgetz Вибачте, я не розмовляю з усіма існуючими мовами, тому не можу відповісти на це
Світлості Гонки в

Я не впевнений, чи застосовуватимуться такі розширення, які неправильно формуються відповідно до цього документа . Вони додали перевантаження, яка сприймає std::vector<bool>::referenceтак, що насправді нічого не складається. Мені здається, що використання чогось подібного char * foo = "bar";вимагає діагностики, оскільки це неправильно формується.
NathanOliver
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.