Почему был изобретен зависимый от аргумента поиск?

Почему был изобретен зависимый от аргумента поиск (ADL)? Это просто так мы можем написать cout << stuff вместо std::operator<<(cout, stuff)? Если это так, то почему ADL не ограничивается операторами вместо всех функций?

Могло бы быть предотвращено введение ADL, если бы в C++ был какой-то другой способ сделать общий вывод как встроенных, так и пользовательских типов, например, безопасных типов printf через вариадические шаблоны?

5 ответов

Решение

ADL был изобретен, чтобы позволить принцип интерфейса:

Принцип интерфейса

Для класса X все функции, включая свободные функции, которые оба

  • "упомянуть" X, и
  • "снабжены" X

логически являются частью X, потому что они образуют часть интерфейса X.

Проверьте превосходного Гуру Недели Херба Саттера на эту тему.

Если это так, то почему ADL не ограничивается операторами вместо всех функций?

Зачем ограничивать это искусственно? ADL может быть сложно реализовать (в сочетании с правилами перегрузки C++)1, но это чрезвычайно полезный метод. Во-первых, тот факт, что он также работает с функциями, значительно упрощает использование других пространств имен без импорта всего пространства имен.

Например, библиотека SeqAn:

using seqan::String;
String<Char> url = "http://www.seqan.de/";
std::cout << "The URL " << url << " has length " << length(url) << std::endl;

Обратите внимание, что я использовал функцию seqan::length без уточнения его полного имени. ADL все равно находит это. Библиотека SeqAn чрезмерно использует такие функции пространства имен, и префикс каждого использования с именем пространства имен будет нецелесообразным. Аналогично, импорт пространства имен часто не рекомендуется.

То же самое, конечно, верно для многих других библиотек, таких как большинство библиотек Boost.


1 И я полагаю, что это не было немедленно понято комитетом.

Почему бы не ограничить это для операторов?

Давайте посмотрим на простой общий алгоритм:

template <typename FwdIt, typename T>
FwdIt remove(FwdIt first, FwdIt last, T const& value)
{
  using std::swap;

  FwdIt result = first;
  for ( ; first != last; ++first)
    if (!(*first == value)) swap(*result++, *first);
  return result;
}

Как это работает с пользовательскими типами и их собственной версией swap? Благодаря ADL.

Это то, что Саттер называет " Интерфейсным принципом", как упоминал Даниэль.

Это было изобретено, чтобы позволить полиморфизм функции. Идея состоит в том, что функция, являющаяся глаголом (например, "печать"), имеет только одно значение. Тем не менее, может быть различная реализация в зависимости от того, к чему применяется глагол (например, int, float и std::string).

Поэтому нам нужно одно слово для концепции, но несколько реализаций в зависимости от того, к чему она применяется.

То, к чему он применяется, является аргументом (ами). Таким образом, нам нужен был способ использовать одно и то же слово в нескольких различных типах аргументов с - где это необходимо - реализацией относительного типа аргумента.

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

Другая причина в том, что он позволяет проверить, какая реализация доступна для типа аргумента. Если нет доступной реализации (даже не универсальной - с использованием шаблонов), тогда компилятор остановит вас, как только сможет, и даст вам понять, что у него нет реализации вашего глагола для данного аргумента.

Да, в основном это было придумано для операторов. Но это также дает вам возможность добавлять функции, не являющиеся членами, в интерфейс вашего класса. И это очень мощная вещь, которая мне очень нравится. И это больше не ограничивается операторами. Например, вы можете определить cross_product функция для вашего vector класс - ты понимаешь о чем я:)

Другие вопросы по тегам