В этом примере программы я наблюдаю другое поведение в g ++ и clang
foo.h:
#include <iostream>
namespace Bar
{
class Foo
{
public:
Foo(int x) : _x(x)
{}
int x() const
{
return _x;
}
private:
int _x;
};
}
std::ostream& operator <<(std::ostream& os, const Bar::Foo* foo);
foo.cpp
#include <Foo.h>
using namespace std;
ostream& operator <<(ostream& os, const Bar::Foo* foo)
{
return os << foo->x();
}
main.cpp
#include <iostream>
using namespace std;
template<typename T>
void print(const T& t)
{
cout << t << endl;
}
#include <Foo.h>
int main(int argc, char** argv)
{
Bar::Foo* foo = new Bar::Foo(5);
print(foo);
}
Компиляция с использованием clang ++ и g ++ дает разные результаты:
air:~ jose$ clang++ Foo.cpp main.cpp -I.
air:~ jose$ ./a.out
0x7ff9e84000e0
air:~ jose$ g++ Foo.cpp main.cpp -I.
air:~ jose$ ./a.out
5
Какой из них правильный и почему?
В этом конкретном случае, Clang ++ является правильным.
Проблема в том, как поиск выполняется внутри шаблона print
, В выражении внутри print
призыв к operator<<
является зависимый. Разрешение имени для зависимые имена обрабатывается в 14.6.4:
При разрешении зависимых имен учитываются имена из следующих источников:
— Объявления, которые видны в точке определения шаблона.
— Объявления из пространств имен, связанных с типами аргументов функции, как из контекста экземпляра (14.6.4.1), так и из контекста определения.
В вашем случае объявление вашего оператора не видно в точке определения шаблона, так как заголовок включается впоследствии, и он не живет ни в одном из связанных пространств имен аргументов функции (а именно ::std
за ::std::ostream
а также ::Bar
за ::Bar::Foo*
), поэтому он не будет найден.
Теперь есть перегрузка в ::std
это занимает void*
и это будет найдено с помощью Argument Dependent Lookup. ::Bar::Foo*
будет преобразован в void*
и адрес будет напечатан.
То есть в стандартном компиляторе.
Я забыл добавить это здесь, и оставил это только в комментарии, но это достаточно важно:
Всегда Определите операторы, которые применяются к вашим типам в том же пространстве имен, которое содержит типы, к которым они применяются. Пусть Argument Dependent Lookup сделает это волшебным для вас. Он был специально разработан для этой конкретной цели, используйте его.
Других решений пока нет …