Различное поведение для квалифицированного и неквалифицированного поиска имени для шаблона
Как должен вести себя этот код? Он вызывает общую функцию, игнорирующую мою перегрузку, если я использую квалифицированное имя в функции call_read()
; и он сначала вызывает перегрузку, а затем общую версию, если я использую неквалифицированное имя. Какая разница? Это ошибка в GCC?
#include <iostream>
struct info1 {};
struct info2 {};
template<class T> void read(T& x)
{
std::cout << "generic" << std::endl;
}
template<class T> void call_read(T& x)
{
::read(x); // if I replace ::read(x) with read(x) the overload is called
}
void read(info1& x)
{
std::cout << "overload" << std::endl;
}
int main()
{
info1 x;
info2 y;
call_read(x);
call_read(y);
}
Я также заметил, что он работает по-разному для фундаментальных типов.
См. Ниже приведенный ниже код
#include <iostream>
typedef struct info1 {};
typedef struct info2 {};
typedef int info3;
typedef double info4;
template<class T> void read(T x)
{
std::cout << "generic" << std::endl;
}
template<class T> void call_read(T x)
{
read(x);
}
void read(info1 x)
{
std::cout << "overload" << std::endl;
}
void read(info3 x)
{
std::cout << "overload" << std::endl;
}
int main()
{
call_read(info1());
call_read(info2());
call_read(info3());
call_read(info4());
}
Предполагается, что функция перегружать дважды, но это не так.
См. Результат здесь
http://codepad.org/iFOOFD52
Ответы
Ответ 1
То, что вы наблюдаете, представляет собой суперпозицию поиска по двухфазному имени и поиска по зависимым от аргументов.
Посмотрим, что говорит стандарт (С++ 03). [Temp.dep]:
[...] В выражении вида:
postfix-expression ( expression-listopt )
где постфиксное выражение является идентификатором, идентификатор обозначает зависимое имя тогда и только тогда, когда любое из выражений в списке выражений является зависимым от типа выражением (14.6.2.2).
Это означает, что как в read
, так и ::read
, read
является зависимым именем, потому что x
зависит от типа. Это означает, что он разрешился в момент создания экземпляра. Посмотрим, какие правила для этого [temp.dep.candidate]:
Для вызова функции, который зависит от параметра шаблона, если имя функции является неквалифицированным идентификатором, но не идентификатором шаблона, функции-кандидаты определяются с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением что:
- Для части поиска, использующей поиск неквалифицированного имени (3.4.1), найдены только объявления функций с внешней связью из контекста определения .
Поэтому для случая ::read
рассматриваются только функции, объявленные до определения шаблона. Но:
- Для части поиска с использованием связанных пространств имен (3.4.2) только объявления функций с внешней привязкой, найденные либо в контексте определения, либо в шаблоне контекст контекста.
для неквалифицированной read
рассматриваются обе функции, видимые при определении шаблона и создании шаблона.
Ответ 2
Да, это ожидаемое поведение. В первом случае (:: read) вы эффектно отключите ADL (зависимый от аргумента поиск), который ограничивает поиск имени теми вещами, которые были объявлены в глобальной области до использования вами чтения. Если вы удалите:: ADL будет ударять, что может разрешить функции, которые вы объявили после вашего шаблона функции.
Изменить: И поскольку для таких фундаментальных типов, как int
и double
нет ADL, это объясняет ваше второе наблюдение.
Ответ 3
Компилятор всегда будет вызывать метод, который наиболее соответствует вашему звонку. Здесь вы вызываете:
read(T& x) [with T = info1]
Таким образом, компилятор предпочтет перегрузку, поскольку он точно соответствует вызову. Это в логике специализированных шаблонов, что позволяет утверждать, что если существует перегруженная функция, которая лучше соответствует вашему вызову, то этот будет использоваться.
Во второй части вопроса, касающемся разницы при использовании полностью квалифицированных и неквалифицированных имен, это происходит из-за того, что полностью квалифицированное имя не зависит ни от чего другого и поэтому разрешено к первому совпадению (здесь объявление шаблона).