точка создания и привязка имени

Меня смущает точка создания экземпляра в следующем примере:

#include <iostream>

void f(int){std::cout<<"int"<<std::endl;}//3

template <typename T>
void g(T t)
{
    f(t);//4
}

void f(double){std::cout<<"double"<<std::endl;}

int main()
{
    g<int>(1);//1.point of instantiation for g<int>
    g<double>(1.1);//2.point of instantiation for g<double>, so f(double) is visible from here?
    return 0;
}

Я, хотя f является зависимым именем, и 1. - это точка создания экземпляра для g ‹int>, а 2. - точка создания экземпляра для g‹ double>, поэтому f (double) виден для g (1.1), однако вывод является

int
int

и если я прокомментирую объявление f (int) в 3, gcc сообщает об ошибке (не удивительно) и указывает, что f (t) в 4 является точкой создания экземпляра (удивлен !!).

test.cpp: In instantiation of ‘void g(T) [with T = int]’:
test.cpp:16:10:   required from here
test.cpp:9:5: error: ‘f’ was not declared in this scope, and no    declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
  f(t);
     ^

Может ли кто-нибудь прояснить для меня концепцию точки создания и привязки имен?


person Shane Nian    schedule 28.05.2015    source источник
comment
Только ADL выполняется из POI (и он не найдет второй f для основных типов, которые не имеют связанных пространств имен). Не-ADL часть неквалифицированного поиска выполняется с точки определения.   -  person dyp    schedule 28.05.2015
comment
Перенос определения f(double) до определения вашего g метода, похоже, решает проблему.   -  person tadman    schedule 28.05.2015
comment
Да, но я хочу сказать, что ADL должен быть в состоянии найти f(double), даже если он появляется после определения g. Объяснение 0x499602D2 кажется правильным.   -  person Shane Nian    schedule 28.05.2015


Ответы (1)


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

f(double) не может быть найден ADL, поскольку встроенные типы не имеют связанных классов или пространств имен. Если вы передали аргумент типа класса и произошла перегрузка f, принимающая этот тип, ADL сможет его найти. Например:

void f(int);

template <typename T>
void g(T t)
{
    f(t);
}

class A {};
void f(double);
void f(A);

int main()
{
    g(1);   // calls f(int)
    g(1.1); // calls f(int)
    g(A{}); // calls f(A)
}

f(A) вызывается, потому что он расположен в глобальном пространстве имен, а связанный набор пространств имен A является глобальным пространством имен.

person 0x499602D2    schedule 28.05.2015
comment
поставьте class A {}; перед void f(A);, тогда все работает нормально, спасибо. - person Shane Nian; 28.05.2015