Принятый ответ просто неверен - это не ошибка ADL. Он показывает небрежный анти-шаблон для использования вызовов функций в ежедневном кодировании - игнорирование зависимых имен и слепое использование неквалифицированных имен функций.
Короче говоря, если вы используете неквалифицированное имя в postfix-expression
вызова функции, вы должны признать, что вы предоставили возможность переопределения функции в другом месте (да, это разновидность статического полиморфизма). Таким образом, написание неквалифицированного имени функции в C ++ является точно частью интерфейса .
В случае принятого ответа, если print_n
действительно нужен ADL print
(т. Е. Допускается его переопределение), он должен был быть задокументирован с использованием неквалифицированного print
в качестве явного уведомления, таким образом клиенты получат контракт о том, что print
должен быть тщательно объявлен, а за ненадлежащее поведение будет отвечать my_stuff
. В противном случае это ошибка print_n
. Исправление простое: квалификация print
с префиксом utility::
. Это действительно ошибка print_n
, но вряд ли это ошибка правил ADL в языке.
Тем не менее, do существует нежелательные вещи в спецификации языка, и технически, не только один . Они реализуются более 10 лет, но ничего в языке еще не исправлено. Они пропущены принятым ответом (за исключением того, что последний абзац до сих пор является правильным). Подробнее см. бумагу .
Я могу добавить один реальный случай против поиска имени. Я реализовывал is_nothrow_swappable
, где __cplusplus < 201703L
. Я обнаружил, что невозможно полагаться на ADL при реализации такой функции, когда у меня есть объявленный шаблон функции swap
в моем пространстве имен. Такой swap
всегда будет найден вместе с std::swap
, введенным идиоматическим using std::swap;
для использования ADL в соответствии с правилами ADL, и тогда возникнет неоднозначность swap
, где шаблон swap
(который будет создавать экземпляр is_nothrow_swappable
чтобы получить правильный noexcept-specification
) называется. В сочетании с правилами двухфазного поиска порядок объявлений не учитывается после включения заголовка библиотеки, содержащего шаблон swap
. Таким образом, если я не перегружу все типы моей библиотеки специализированной функцией swap
(для подавления любых подходящих шаблонных шаблонов swap
, сопоставляемых с перегрузкой разрешения после ADL), я не могу объявить шаблон. По иронии судьбы, шаблон swap
, объявленный в моем пространстве имен, предназначен именно для использования ADL (рассмотрим boost::swap
), и это один из наиболее значимых прямых клиентов is_nothrow_swappable
в моей библиотеке (кстати, boost::swap
не учитывает исключение Спецификация). Это прекрасно побило мою цель, вздох ...
#include <type_traits>
#include <utility>
#include <memory>
#include <iterator>
namespace my
{
#define USE_MY_SWAP_TEMPLATE true
#define HEY_I_HAVE_SWAP_IN_MY_LIBRARY_EVERYWHERE false
namespace details
{
using ::std::swap;
template<typename T>
struct is_nothrow_swappable
: std::integral_constant<bool, noexcept(swap(::std::declval<T&>(), ::std::declval<T&>()))>
{};
} // namespace details
using details::is_nothrow_swappable;
#if USE_MY_SWAP_TEMPLATE
template<typename T>
void
swap(T& x, T& y) noexcept(is_nothrow_swappable<T>::value)
{
// XXX: Nasty but clever hack?
std::iter_swap(std::addressof(x), std::addressof(y));
}
#endif
class C
{};
// Why I declared 'swap' above if I can accept to declare 'swap' for EVERY type in my library?
#if !USE_MY_SWAP_TEMPLATE || HEY_I_HAVE_SWAP_IN_MY_LIBRARY_EVERYWHERE
void
swap(C&, C&) noexcept
{}
#endif
} // namespace my
int
main()
{
my::C a, b;
#if USE_MY_SWAP_TEMPLATE
my::swap(a, b); // Even no ADL here...
#else
using std::swap; // This merely works, but repeating this EVERYWHERE is not attractive at all... and error-prone.
swap(a, b); // ADL rocks?
#endif
}
Попробуйте https://wandbox.org/permlink/4pcqdx0yYnhhrASi и поверните USE_MY_SWAP_TEMPLATE
на true
, чтобы увидеть неоднозначность.
Обновление 2018-11-05:
Ага, сегодня утром меня снова укусила ADL. На этот раз это даже не имеет отношения к вызовам функций!
Сегодня я заканчиваю работу по переносу ISO C ++ 17 std::polymorphic_allocator
на мою кодовую базу. Поскольку некоторые шаблоны классов контейнеров были введены в моем коде давно (например, this ), на этот раз я просто заменяю объявления шаблонами псевдонимов, такими как:
namespace pmr = ystdex::pmr;
template<typename _tKey, typename _tMapped, typename _fComp
= ystdex::less<_tKey>, class _tAlloc
= pmr::polymorphic_allocator<std::pair<const _tKey, _tMapped>>>
using multimap = std::multimap<_tKey, _tMapped, _fComp, _tAlloc>;
... поэтому он может использовать мою реализацию polymorphic_allocator
по умолчанию. (Отказ от ответственности: в нем есть некоторые известные ошибки. Исправления ошибок будут совершены через несколько дней.)
Но это вдруг не работает, с сотнями строк загадочных сообщений об ошибках ...
Ошибка начинается с этой строки . Он грубо жалуется на то, что заявленное BaseType
не является основой класса включения MessageQueue
. Это кажется очень странным, потому что псевдоним объявляется с точно такими же токенами, что и в base-specier-list-list определения класса, и я уверен, что ничего из них не может быть расширено макросом. Так почему?
Ответ ... ADL отстой. Строка, представляющая BaseType
, жестко запрограммирована с именем std
в качестве аргумента шаблона, поэтому шаблон будет найден в соответствии с правилами ADL в области видимости класса . Таким образом, он находит std::multimap
, который отличается от результата поиска как фактический базовый класс, объявленный во включающей области имен . Поскольку std::multimap
использует std::allocator
экземпляр в качестве аргумента шаблона по умолчанию, BaseType
отличается от фактического базового класса, который имеет экземпляр polymorphic_allocator
, даже multimap
, объявленный в окружающем пространстве имен, перенаправляется на std::multimap
. При добавлении включающей квалификации в качестве префикса справа к =
ошибка исправлена.
Я признаю, что мне повезло. Сообщения об ошибках направляют проблему на эту строку. Есть только 2 аналогичные проблемы, и другой без каких-либо явных std
(где string
- мой собственный , адаптируемый к изменению string_view
ISO C ++ 17, не std
один в режимах до C ++ 17). Я бы не понял, ошибка в ADL так быстро.