Перегрузка операторов clang ++ и g ++ отличается выводом

В этом примере программы я наблюдаю другое поведение в 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

Какой из них правильный и почему?

9

Решение

В этом конкретном случае, 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 сделает это волшебным для вас. Он был специально разработан для этой конкретной цели, используйте его.

13

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]