Что такое поиск имен в С++? (& GCC правильно?)

У меня возникла проблема в каком-то производственном коде, который я минимизировал до следующего тестового примера:

template<typename T>
void intermediate(T t)
{
    func(t); // line 4 ("func not declared in this scope")
}

namespace ns {
    struct type {};
}

void func(ns::type const & p); // line 11 ("declared here, later")

void foo(ns::type exit_node)
{
    intermediate(exit_node);  // line 15 ("required from here")
}

GCC 4.5 компилирует этот штраф. Как с, так и без -std=c++11, 4.7 и 4.9 вызывают такие сообщения, как:

test.cpp: In instantiation of ‘void intermediate(T) [with T = ns::type]’:
test.cpp:15:27:   required from here
test.cpp:4:5: error: ‘func’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
test.cpp:11:6: note: ‘void func(const ns::type&)’ declared here, later in the translation unit

Все три следующие вещи приведут к успешному компиляции файла:

  • Переместите func(ns::type) в пространство имен ns (позволяя ADL найти его в ns)
  • Переместите type в глобальное пространство имен (позволяя ADL найти его в ::)
  • Избавьтесь от intermediate и вызовите func непосредственно из foo

Итак... что здесь происходит? Является ли законным для GCC отказаться от этой программы? Почему func найден неквалифицированным поиском в третьем варианте (вызов func непосредственно из foo), но не найден неквалифицированным поиском в исходном варианте в момент создания экземпляра?

Ответ 1

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

Поскольку объявление func не отображается, если intermediate определено, а func не находится в пространстве имен, ассоциированном с ns::type, код плохо сформирован.

Ответ 2

GCC прав. func можно найти только через ADL, так как это неквалифицированный, зависимый вызов функции. func объявляется в глобальном пространстве имен, но это не связанное с ним пространство имен ns::type, а только ns (поэтому ваш текущий код не работает). Когда вы заменяете intermediate(exit_node) на прямой вызов func(exit_node) внутри foo, то он обнаруживается обычным неквалифицированным поиском.