У меня есть три файла 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
во что-нибудь еще, ошибка компилятора исчезает. Почему это?
Потому что поиск прекращается, как только обнаруживается lerp
. Здесь, когда вы написали x = lerp(t, x, 0.f);
, поиск начинается вверх и находит функцию-член с именем lerp
, а затем поиск останавливается. То есть поиск не видит глобальную версию. А поскольку найденная версия несовместима, мы получаем упомянутую ошибку.
почему компилятор, похоже, не может найти определение MyNamespace::lerp(), указанное в header1.h?
Потому что, когда вы написали x = lerp(t, x, 0.f);
, начинается поиск неквалифицированного имени и находит функцию-член с именем lerp
, и поэтому поиск имени останавливается. Таким образом, единственной функцией-кандидатом является функция-член с именем lerp
. А поскольку это несовместимо с аргументами вызова, мы получаем упомянутую ошибку.
Это видно из документации по поиску неполных имен:
Для неполного имени, то есть имени, которое не отображается справа от оператора разрешения области ::, поиск имени проверяет области, как описано ниже, пока не найдет хотя бы одно объявление любого типа, после чего поиск прекращается. и никакие дальнейшие области применения не рассматриваются.
Имя члена класса будет скрывать все другие неквалифицированные варианты использования имени. Таким образом, вам нужно квалифицировать это или назвать как-то иначе. Именно так всегда работал C++.