У меня есть следующий код C++, который выводит 12
:
#include <iostream>
using namespace std;
template<typename T>
T foo(T val) {
return val;
}
template<typename T>
T bar(T n) {
return foo(n);
}
int foo(int val) {
return 1;
}
int main() {
cout<<foo(2)<<bar(2)<<endl;
return 0;
}
Если я изменю определение bar
на
int bar(int n) {
return foo(n);
}
не меняя своего положения, результат по-прежнему будет 12
.
Однако, если я перенесу определения bar
непосредственно перед main
, результат изменится на 11
. (составитель: g++ 14.1.1
)
Мой вопрос:
bar(2)
вызывает шаблонную версию foo
вместо перегрузки без шаблона, даже если обе они кажутся жизнеспособными при создании экземпляра bar
?11
, не меняя порядок определений функций, сохраняя при этом общность определения шаблона foo?Также по одному вопросу в каждом посте. C++ — очень сложный язык, и ответ на один и тот же вопрос может сильно отличаться. Вы рассматриваете так много дел одновременно.
Двухэтапный поиск имени. Имя foo
ищется в точке определения bar
и обнаруживается перегрузка шаблона; перегрузка без шаблона пока не видна. Имя снова ищется в момент создания экземпляра, но только с использованием поиска, зависящего от аргумента. А поскольку int
не имеет связанного пространства имен, нешаблонная перегрузка foo
не найдена.
Почему
bar(2)
вызывает шаблонную версию foo
В исходном случае в момент вызова foo(n)
жизнеспособна только версия шаблона функции foo
. Несмотря на то, что существует двухфазный поиск, для int
не существует связанного пространства имен, поскольку int
является встроенным типом. То есть нешаблонная версия здесь не добавляется в список кандидатов.
Если бы у вас был параметр типа класса вместо int
, вызов foo(n)
в исходном случае вызвал бы нешаблонный foo
(демо ), потому что на этот раз зависимый от аргумента поиск в фазе двое увидели бы, что нешаблон foo
также является жизнеспособным кандидатом.
Во втором случае, когда вы перемещаете определение bar
непосредственно перед main
, для foo(n)
видны обе версии (шаблонная и нешаблонная). А поскольку версия без шаблона предпочтительнее версии с шаблоном, мы видим 11
.
Как я могу изменить код для вывода
11
, не меняя порядок определений функций, сохраняя при этом общность определения шаблона foo?
Вы можете добавить предварительное объявление для нешаблонного foo
перед bar
, как показано ниже:
#include <iostream>
using namespace std;
template<typename T>
T foo(T val) {
return val;
}
int foo(int val); //added this declaration
template<typename T>
T bar(T n) {
return foo(n);
}
int foo(int val) {
return 1;
}
int main() {
cout<<foo(2)<<bar(2)<<endl; //now this prints 11
return 0;
}
«Почему bar(2) вызывает версию шаблона foo...» Потому что в точке
foo(n)
видна только версия шаблона функцииfoo
. Так что нет другого выхода, кроме как вызвать шаблонную версию.