LINUX.ORG.RU

swap функция, ranges lib, ambiguous call

 


0

2

Потихоньку трогаю рейнджи, и то ли я прогулял что-то, то ли просто не понял. Почему здесь неоднозначный вызов?

#include <ranges>
#include <utility>

class Ustring {
    unsigned char m_markup_state;
    friend void swap(Ustring &l, Ustring &r);
};

void swap(Ustring &l, Ustring &r)
{
    //using std::ranges::swap;     // ok
    //using namespace std;         // ok
    using namespace std::ranges;
    swap(l.m_markup_state, r.m_markup_state); // ambiguous call here
}

int main() {
}

$ g++ 1.cpp -std=c++20
1.cpp:14:9: error: reference to ‘swap’ is ambiguous
...concepts:229:43: note: candidates are: ‘constexpr const std::ranges::__cust_swap::_Swap std::ranges::__cust::swap’
1.cpp:9:6: note: ‘void swap(Ustring&, Ustring&)’

Если использовать по старинке std’шный swap() через using namespace std - то неоднозначностей нет. Можно заставить работать и рейнджи через using std::ranges::swap вместо using namespace, но я не понимаю почему оно вот так вот работает, как swap функция для какого-то там Ustring вообще может быть годна для unsigned char?

★★

Последнее исправление: kvpfs (всего исправлений: 1)

А ты вызови эту функцию (swap для Ustring) напрямую без ambigous и посмотри что она будет делать с unsigned char'ом.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Так я её в примере вообще не вызывал, неоднозначность внутри тела своп(Юстринг &, Юстринг &). Можно заменить вызовы на qualified swap (std::ranges::swap()), и в случае ренджов это должно правильно работать в отличии от std::swap, но я не про «как написать чтобы работало», а про «почему не работает так», не знаю, для меня секрет.

kvpfs ★★
() автор топика
Последнее исправление: kvpfs (всего исправлений: 1)

как swap функция для какого-то там Ustring вообще может быть годна для unsigned char?

У тебя лукап ambiguous, а не overload resolution.

anonymous
()

Если использовать по старинке std’шный swap() через using namespace std - то неоднозначностей нет.

Потому что когда находится твой swap и std::swap — оба функции. Несколько функций (да даже одна) образуют overload set. Это ОК.

Когда находится твой swap и std::ranges::...::swap, то второе — это переменная. Тогда lookup is ambiguous.

Post-P1787 [basic.lookup.general]/1:

If the declarations found by name lookup all denote functions or function templates, the declarations are said to form an overload set. Otherwise, if the declarations found by name lookup do not all denote the same entity, they are ambiguous and the program is ill-formed.

utf8nowhere ★★★
()
Последнее исправление: utf8nowhere (всего исправлений: 2)

Ну говорили же - не давай имён, совпадающих с библиотечными, а он не слушал. А в конкретно данном случае, если я правильно понимаю у тебя получается что различаются функции по числу шаблонных параметров

template<typename T, typename U>
std::ranges::swap

template<typename T>
std::swap

и твоя void swap получается перегружает вторую, но понять к какой из двух библиотечных она принадлежит не так очевидно, т.к. может быть как U=T=Ustring, так и просто T=Ustring

AKonia ★★
()
Последнее исправление: AKonia (всего исправлений: 2)
Ответ на: комментарий от utf8nowhere

Когда находится твой swap и std::ranges::…::swap, то второе — это переменная. Тогда lookup is ambiguous.

Да, похоже, что так и есть. В общем-то знал, что std::ranges::swap - переменная, но как-то в подобные кейсы с неоднозначным lookup’ом не попадал. Спасибо.

kvpfs ★★
() автор топика

Наглядный пример того, в чём разница между using-declaration (using std::ranges::swap) и using-directive (using namespace std::ranges).

Второй начинает «работать» только когда поиск выбирается в глобальный неймспейс. И тогда находится и ::swap, и ::std::ranges::swap.

И происходит что-то вроде

namespace N
{
    float f = 0;
}

void f()
{
    using namespace N;
    f();
}
<source>:9:5: error: reference to 'f' is ambiguous
    f();
    ^
<source>:6:6: note: candidate found by name lookup is 'f'
void f()
     ^
<source>:3:11: note: candidate found by name lookup is 'N::f'
    float f = 0;
          ^
utf8nowhere ★★★
()
Последнее исправление: utf8nowhere (всего исправлений: 3)
Ответ на: комментарий от AKonia

Ну говорили же - не давай имён, совпадающих с библиотечными, а он не слушал.

Подобное делали от царя гороха:

using std::swap;
swap(x, y);   // Если ADL не найдёт спец версию, то берём из std

ragnes же, как я понимаю, называет такие места «customization point object», и внутри этого объекта сама диспетчеризует вызов в нужное место - либо спец версию юзера, либо в std. Также там обещают проверку концептов, и человекощадящие ошибки.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

так перегрузка и переопределение - разные вещи. Да и без разницы что там думает царь, вопрос в том, одно дело честно создать свою специализацию, другое дело тупо перегрузить, что для библиотечных функций - дурной тон.

AKonia ★★
()
Ответ на: комментарий от AKonia

friend swap это то, как принято делоть. Нужно просто аккуратнее вываливать его определение в namespace scope.

anonymous
()
Ответ на: комментарий от kvpfs

ragnes же, как я понимаю, называет такие места «customization point object», и внутри этого объекта сама диспетчеризует вызов в нужное место - либо спец версию юзера, либо в std. Также там обещают проверку концептов, и человекощадящие ошибки.

И которые предполагается вызывать используя fully qualified name, без using ….

anonymous
()
Ответ на: комментарий от anonymous

И которые предполагается вызывать используя fully qualified name, без using ….

Спорно, я не вижу плохого в заюзывании unqualified name + using, это улучшает в читаемость. Например, надо свопнуть 5 объектв в старшем свопе:

void swap(...) {
   std::ranges::swap(l.var, r.var);
   std::ranges::swap(...);
   std::ranges::swap(...);
   std::ranges::swap(...);
   std::ranges::swap(...);
}
vs
void swap(...) {
   using std::ranges::swap;
   swap(l.var, r.var);
   swap(...);
   swap(...);
   swap(...);
   swap(...);
}

Очевидно, что второе много симпатичней, -Wall -Wpedantic -Wshadow ворнингов не дают, зачем придумывать себе какие-то рамки и делать по уродски? using-declaration делает std::ranges::swap будто объявленным в том же scope, т.е. самым приоритетным, пока проблем не вижу.

kvpfs ★★
() автор топика
Ответ на: комментарий от fsb4000
// correct
std::ranges::swap(a, b);

// same thing, just as correct
using namespace std::ranges;
swap(a, b);

same thing, just as correct

:))))

Да, по этому топику видно.

anonymous
()
Ответ на: комментарий от anonymous

Оттуда же:

For instance, Matt Calabrese’s Customization Point Functions proposal would allow you to declare a function final to get this desired ADL-inhibiting behavior.

Возможно решится в будущем, niebloids перепишут на функциях, и using namespace std::range; +swap() внутри другого свопа станет валиден. Пока так.

kvpfs ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.