У меня есть следующий код 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. Так что нет другого выхода, кроме как вызвать шаблонную версию.