Подтвердить что ты не робот

Почему ADL не разрешает правильную функцию с помощью std:: get

Я пытаюсь закодировать функцию шаблона, которая использует разрешенный ADL get для извлечения элементов структуры/диапазона (tuple -esque).

#include <iostream>
#include <utility>
#include <tuple>

int main() {
    auto tup = std::make_tuple(1, 2);
    std::cout << get<0>(tup) << std::endl;
}

Я делаю это из-за того, что предложение структурированных привязок (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf §11.5.3) говорит о том, как используется get для извлечения элементов из структуры. В нем говорится, что для элемента изнутри структуры используется элемент non member get.

Я предположил, что код выше будет компилироваться, поскольку ADL приведет к тому, что функция get будет искать в пространстве имен std (поскольку аргумент имеет тип std::tuple<int, int>, который находится в std), где он будет найден. Но я получаю сообщение об ошибке. Может ли кто-то объяснить правильный подход здесь, а также почему код выше не работает? Как можно заставить ADL произойти в этом случае?

4b9b3361

Ответ 1

В конечном итоге проблема заключается в шаблонах:

std::cout << get<0>(tup) << std::endl;
//           ~~~~

В этот момент компилятор не знает, что это функция, которую нужно искать с помощью ADL еще - get - это просто имя. И поскольку это имя само по себе ничего не находит, это будет интерпретироваться как неизвестное имя, за которым следует меньшее. Чтобы заставить это работать, вам нужен еще один шаблон функции get:

using std::get;
std::cout << get<0>(tup) << std::endl; // now, OK

Даже если он ничего не делает:

template <class T> void get();

int main() {
    auto tup = std::make_tuple(1, 2); 
    std::cout << get<0>(tup) << std::endl;
}

Строгосвязанная формулировка явно ищет get, используя зависящий от аргумента поиск, поэтому он избегает необходимости иметь уже видимый шаблон функции с именем get, из [dcl.struct.bind]:

Unqualified-id get просматривается в области E путем поиска доступа к члену класса, и если он находит хотя бы одно объявление, инициализатор e.get<i>(). В противном случае инициализатор get<i>(e), где get просматривается в связанных пространствах имен. В любом случае get<i> интерпретируется как идентификатор шаблона. [Примечание: Обычный неквалифицированный поиск не выполняется. - конечная нота]

Примечание - это ключ. Если бы мы выполнили безусловный поиск, мы бы просто потерпели неудачу.

Ответ 2

Поиск зависимых от аргументов не работает так же для шаблонов функций, где задан явный аргумент шаблона.

Хотя вызов функции может быть разрешен посредством ADL, даже если обычный lookup ничего не находит, вызов функции для шаблона функции с явно заданные аргументы шаблона требуют, чтобы объявление шаблона, найденное обычным поиском (в противном случае это синтаксическая ошибка, чтобы встретить неизвестное имя, за которым следует меньше, чем символ)

В принципе, для безошибочного поиска должен быть какой-то способ найти функцию шаблона. Затем ADL может ударить (поскольку имя get, как известно, является шаблоном). Cppreference дает пример:

namespace N1 {
  struct S {};
  template<int X> void f(S);
}
namespace N2 {
  template<class T> void f(T t);
}
void g(N1::S s) {
  f<3>(s);      // Syntax error (unqualified lookup finds no f)
  N1::f<3>(s);  // OK, qualified lookup finds the template 'f'
  N2::f<3>(s);  // Error: N2::f does not take a non-type parameter
                //        N1::f is not looked up because ADL only works
                //              with unqualified names
  using N2::f;
  f<3>(s); // OK: Unqualified lookup now finds N2::f
           //     then ADL kicks in because this name is unqualified
           //     and finds N1::f
}

Структурированные привязки - это особый случай с включенным ADL.

В следующих контекстах поиск только для ADL (то есть поиск в только связанные пространства имен):

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

Добавлен акцент