2013-04-12 4 views
9

С помощью этой программы образца я наблюдаю другое поведение в г ++ и лязгПерегрузка операторов лязг ++ и г ++ отличается выход

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(); 
} 

основной. 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 

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

+0

Это может быть проще для людей, если вы разрешили '# include' для одного пастыруемого объекта. – PlasmaHH

+0

Единственная часть этого я нахожу интересной, что она даже работает. 'Operator <<' принимает 'Bar :: Foo const *', но с помощью шаблона 'print()' вы отправляете 'Bar :: Foo * const' (ссылка в этом случае несущественна).Выньте шаблон и просто отправьте указатель-указатель на 'cout' и посмотрите, что вам дают обе привязки. * Лучше * быть одинаковым. – WhozCraig

+0

Какой бы ни был прав, ваш код * неправильный * (или, по крайней мере, явно улучшенный). Вы должны объявить/определить, что 'operator <<' внутри пространства имен 'Bar', так что поиск аргумента зависит от него. –

ответ

13

В этом конкретном случае clang ++ является правильным.

Проблема заключается в том, как поиск выполняется внутри шаблона print. В выражении внутри print вызов operator<< равен зависимым. Разрешение имен для зависимых имен обрабатываются в 14.6.4:

В решении зависимых имен, имен из следующих источников считается:

- Объявления, которые видны в момент определения шаблона ,

- Объявления из пространств имен, связанных с типами аргументов функции как из контекста создания (14.6.4.1), так и из контекста определения.

В вашем случае, декларация вашего оператора не видно в точке определения шаблона, поскольку заголовок включается после этого, и он не живет ни в одном из соответствующих пространств имен аргументов функций (а именно ::std для ::std::ostream и ::Bar для ::Bar::Foo*), поэтому его не найти.

Теперь есть перегрузка в ::std, которая занимает void*, и это будет найдено в зависимости от искателя Аргумент. ::Bar::Foo* будет преобразован в void*, и адрес будет распечатан.

То есть, в стандартном совместимом компиляторе.

Я забыл добавить это здесь, и оставил его только в комментариях, но это достаточно важно:

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

+0

+1, я понял, что ошибаюсь и не думал о '<<', зависящем от –

+0

+1 отличное объяснение –

+0

Я подал ошибку gcc для этого: http: //gcc.gnu .org/bugzilla/show_bug.cgi? id = 56943 – HighCommander4