«В 1985 году было выпущено первое издание языка программирования C ++, которое стало окончательным справочником по языку, поскольку еще не было официального стандарта». wiki История C ++ Таким образом, между C ++ 11 и С ++ 14. Я могу предположить (и, пожалуйста, отнеситесь к этому с долей скепсиса), он изменился между «предварительной стандартизацией» и стандартизацией. Может быть, кто-то, кто лучше знает историю C ++, сможет пролить здесь больше света.
Что же происходит на самом деле:
Сначала давайте выберем простой вариант:
extern g(double);
Это недопустимый C ++. Исторически, к сожалению, C допускал пропуск типа. В C ++ вы должны написать extern void g(double)
.
Затем давайте проигнорируем g(double)
перегрузку, чтобы ответить на ваш первый вопрос:
template <class T>
void f(T value)
{
g(value);
}
void g(int v);
int main()
{
f(2);
}
В C ++ есть печально известный двухэтапный поиск имени:
- На первом этапе, при определении шаблона, разрешаются все независимые имена . Невыполнение этого требования - серьезная ошибка;
- Зависимые имена разрешаются на втором этапе при создании экземпляра шаблона.
Правила немного сложнее, но в этом суть.
g
зависит от параметра шаблона T
, поэтому он проходит первую фазу. Это означает, что если вы никогда не создадите экземпляр f
, код компилируется нормально. На втором этапе f
создается с T = int
. g(int)
теперь ищется, но не найден:
17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup
g(value);
^
24 : note: in instantiation of function template specialization 'f<int>' requested here
f(2);
^
20 : note: 'g' should be declared prior to the call site
void g(int v);
Чтобы произвольное имя g
прошло с честью, у нас есть несколько вариантов:
- Объявить
g
ранее:
void g(int);
template <class T>
void f(T value)
{
g(value);
}
- принесите
g
с T
:
template <class T>
void f(T)
{
T::g();
}
struct X {
static void g();
};
int main()
{
X x;
f(x);
}
- Принесите
g
с T
через ADL:
template <class T>
void f(T value)
{
g(value);
}
struct X {};
void g(X);
int main()
{
X x;
f(x);
}
Это, конечно, меняет семантику программы. Они предназначены для иллюстрации того, что вы можете и чего не можете иметь в шаблоне.
Что касается того, почему ADL не находит g(int)
, а находит g(X)
:
§ 3.4.2 Поиск имени в зависимости от аргумента [basic.lookup.argdep]
Для каждого типа аргумента T в вызове функции существует набор из нуля или более связанных пространств имен и набор из нуля или более связанных классов, которые следует учитывать [...]:
Если T является фундаментальным типом, связанные с ним наборы пространств имен и классов пусты.
Если T является типом класса (включая объединения), то связанные с ним классы: сам класс; класс, членом которого он является, если таковой имеется; и его прямые и косвенные базовые классы. Связанные с ним пространства имен - это пространства имен, членами которых являются связанные с ним классы. [...]
И, наконец, мы выясним, почему extern void g(double);
внутри main не найден: прежде всего мы показали, что g(fundamental_type)
найден iff он объявлен до определения f
. Так что давайте сделаем это void g(X)
внутри main
. АДЛ его находит?
template <class T>
void f(T value)
{
g(value);
}
struct X{};
int main()
{
X x;
void g(X);
f(x);
}
Нет. Поскольку он не находится в том же пространстве имен, что и X
(т. Е. Глобальное пространство имен), ADL не может его найти.
Доказательство того, что g
не входит в глобальную
int main()
{
void g(X);
X x;
g(x); // OK
::g(x); // ERROR
}
34: ошибка: нет члена с именем 'g' в глобальном пространстве имен; Вы имели в виду просто "g"?
person
bolov
schedule
10.10.2016
C++11
иC++14
. Он изменился между предварительной стандартизацией и стандартизацией. - person bolov   schedule 10.10.2016