Поведение при перегрузке функции шаблона меняется в зависимости от порядка объявления функций

У меня есть следующий код 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)

Мой вопрос:

  1. Почему bar(2) вызывает шаблонную версию foo вместо перегрузки без шаблона, даже если обе они кажутся жизнеспособными при создании экземпляра bar?
  2. Как я могу изменить код для вывода 11, не меняя порядок определений функций, сохраняя при этом общность определения шаблона foo?

«Почему bar(2) вызывает версию шаблона foo...» Потому что в точке foo(n) видна только версия шаблона функции foo. Так что нет другого выхода, кроме как вызвать шаблонную версию.

user12002570 11.08.2024 18:31

Также по одному вопросу в каждом посте. C++ — очень сложный язык, и ответ на один и тот же вопрос может сильно отличаться. Вы рассматриваете так много дел одновременно.

user12002570 11.08.2024 18:31

Двухэтапный поиск имени. Имя foo ищется в точке определения bar и обнаруживается перегрузка шаблона; перегрузка без шаблона пока не видна. Имя снова ищется в момент создания экземпляра, но только с использованием поиска, зависящего от аргумента. А поскольку int не имеет связанного пространства имен, нешаблонная перегрузка foo не найдена.

Igor Tandetnik 11.08.2024 18:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почему 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;
}

Другие вопросы по теме