Почему я должен указывать пространство имен глобальной функции при вызове ее из функции-члена с таким же именем в том же пространстве имен?

У меня есть три файла C++ header1.h, header2.h и main.cpp. Они ниже:

header1.h, который определяет функцию lerp в пространстве имен MyNamespace:

#ifndef HEADER_1_H
#define HEADER_1_H

namespace MyNamespace {

    template <typename T>
    inline float lerp(float t, T a, T b) {
        return (1 - t) * a + t * b;
    }

};

#endif

header2.h, который определяет класс MyClass в MyNamespace, который имеет функцию-член lerp, которая вызывает lerp из header1.h:

#ifndef HEADER_2_H
#define HEADER_2_H

#include "header1.h"

namespace MyNamespace {

    struct MyClass {
        float x, y, z;
    
        void lerp(float t) {
            x = lerp(t, x, 0.f);
             // ^^ This results in a compiler error (no matching function for
             // call to ‘MyNamespace::MyClass::lerp(float&, float&, float). Why?
             // Also, either prefixing this `lerp` with MyNamespace, or changing
             // the name of this member function to anything else that's not lerp,
             // stops the compiler error from occurring. Why?
        }
    };

};

#endif

main.cpp, который создает объект типа MyClass и вызывает MyClass::lerp() для него:

#include "header2.h"

using namespace MyNamespace;

int main()
{
    MyClass mc{1, 2, 3};
    mc.lerp(4);  // results in a compiler error; see the comment in "header2.h"

    return 0;
}

Когда я пытаюсь скомпилировать main.cpp, я получаю следующую ошибку компилятора об использовании lerp внутри функции MyClass::lerp() в header2.h:

header2.h: In member function ‘void MyNamespace::MyClass::lerp(float)’:
header2.h:12:17: error: no matching function for call to ‘MyNamespace::MyClass::lerp(float&, float&, float)’
   12 |         x = lerp(t, x, 0.f);
      |             ~~~~^~~~~~~~~~~
header2.h:11:10: note: candidate: ‘void MyNamespace::MyClass::lerp(float)’
   11 |     void lerp(float t) {
      |          ^~~~
header2.h:11:10: note:   candidate expects 1 argument, 3 provided

Итак, мой первый вопрос: почему компилятор не может найти определение MyNamespace::lerp(), представленное в header1.h? Разве он не должен его найти, потому что мы находимся в одном пространстве имен (MyNamespace) и потому что header2.h включает header1.h?

Кроме того, я заметил, что либо явно указывая lerp(t, x, 0.f) с помощью MyNamespace, либо переименовывая функцию-член MyClass::lerp во что-нибудь еще, ошибка компилятора исчезает. Почему это?

Имя члена класса будет скрывать все другие неквалифицированные варианты использования имени. Таким образом, вам нужно квалифицировать это или назвать как-то иначе. Именно так всегда работал C++.

NathanOliver 07.06.2024 05:35

Потому что поиск прекращается, как только обнаруживается lerp. Здесь, когда вы написали x = lerp(t, x, 0.f);, поиск начинается вверх и находит функцию-член с именем lerp, а затем поиск останавливается. То есть поиск не видит глобальную версию. А поскольку найденная версия несовместима, мы получаем упомянутую ошибку.

user12002570 07.06.2024 05:38
Стоит ли изучать 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
2
78
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

tldr Потому что поиск неквалифицированных имен проверяет области, как описано ниже, до тех пор, пока не будет найдено хотя бы одно объявление любого типа, после чего поиск прекращается и дальнейшие области не проверяются.

почему компилятор, похоже, не может найти определение MyNamespace::lerp(), указанное в header1.h?

Потому что, когда вы написали x = lerp(t, x, 0.f);, начинается поиск неквалифицированного имени и находит функцию-член с именем lerp, и поэтому поиск имени останавливается. Таким образом, единственной функцией-кандидатом является функция-член с именем lerp. А поскольку это несовместимо с аргументами вызова, мы получаем упомянутую ошибку.

Это видно из документации по поиску неполных имен:

Для неполного имени, то есть имени, которое не отображается справа от оператора разрешения области ::, поиск имени проверяет области, как описано ниже, пока не найдет хотя бы одно объявление любого типа, после чего поиск прекращается. и никакие дальнейшие области применения не рассматриваются.

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