9

With this sample program I observe a different behavior in g++ and 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);
}

Compiling with clang++ and g++ produce different results:

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

Which one is correct and why?.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
José
  • 3,041
  • 8
  • 37
  • 58
  • It might be easier for people if you resolved the `#include`s to a single pasteable entity. – PlasmaHH Apr 12 '13 at 15:54
  • The only part of this I find interesting that it even works. The `operator<<` takes a `Bar::Foo const *`, but through the `print()` template your sending a `Bar::Foo * const` (the reference is immaterial at that point). Take out the template and just send the pointer-direct to `cout` and see what both toolchains give you. The *better* be the same. – WhozCraig Apr 12 '13 at 16:02
  • Whichever is right, your code is *wrong* (or at least clearly improvable). You should declare/define that `operator<<` inside the namespace `Bar`, so that Argument Dependent Lookup will find it. – David Rodríguez - dribeas Apr 12 '13 at 16:07
  • if i send directly the pointer to cout instead of using "print" both output 5 – José Apr 12 '13 at 16:07
  • @DavidRodríguez-dribeas: While I agree with you that the operator should be part of the `Bar` namespace, this is not the root cause of the issue. The call occurs in the global namespace, so name lookup should find the OP's overload and prefer it over the one accepting a `void const*`. – Andy Prowl Apr 12 '13 at 16:14
  • @AndyProwl: But it is the root cause of the issue :) If the operator cannot be found by Argument Dependent Lookup, then the order of the rest of the code suddenly becomes important and you might end up with surprising results like the output of clang above (which is correct), or more surprisingly with the output of g++ (which is incorrect, but probably *expected*) – David Rodríguez - dribeas Apr 12 '13 at 16:20
  • @DavidRodríguez-dribeas: You are right, I wrote this comment before reading your answer and did not figure out that the declaration was not visible. Well done – Andy Prowl Apr 12 '13 at 16:22
  • @AndyProwl: One of these days I will have enough time to write about lookup... it is one of the *core* things that everyone should know, and while the basics are simple, the fact is that it is surprisingly complex and most people (including me) get confused sooner or later. – David Rodríguez - dribeas Apr 12 '13 at 16:26
  • @DavidRodríguez-dribeas: Agreed. Put me in the list of those people. And please ping me when you will write about it – Andy Prowl Apr 12 '13 at 16:27

1 Answers1

13

In this particular case, clang++ is correct.

The problem is how lookup is performed inside the template print. In the expression inside print the call to operator<< is dependent. Name resolution for dependent names is handled in 14.6.4:

In resolving dependent names, names from the following sources are considered:

— Declarations that are visible at the point of definition of the template.

— Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

In your case, the declaration of your operator is not visible at the point of definition of the template, since the header is included afterwards, and it does not live in any of the associated namespaces of the function arguments (namely ::std for ::std::ostream and ::Bar for ::Bar::Foo*), so it won't be found.

Now, there is an overload in ::std that takes a void*, and that will be found by Argument Dependent Lookup. The ::Bar::Foo* will be converted to a void* and the address will be printed.

That is, in a standard compliant compiler.

I forgot to add this here, and left it only in the comment, but it is important enough:

Always define the operators that apply to your types in the same namespace that holds the types on which they apply. Let Argument Dependent Lookup do it's magic for you. It was specifically designed to serve this particular purpose, use it.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489