9

Someone can explain me a warning from g++ ?

Given the following code

#include <iostream>

namespace foo
 {
   struct bar
    { friend std::ostream & operator<< (std::ostream &, bar const &); };
 }

std::ostream & foo::operator<< (std::ostream & o, foo::bar const &)
 { return o; }

int main ()
 {
   foo::bar  fb;

   std::cout << fb;
 }

I get (from g++ (6.3.0) but not from clang++ (3.8.1) and not (thanks Robert.M) from Visual Studio (2017 community)) this warning

tmp_002-11,14,gcc,clang.cpp:10:16: warning: ‘std::ostream& foo::operator<<(std::ostream&, const foo::bar&)’ has not been declared within foo
 std::ostream & foo::operator<< (std::ostream & o, foo::bar const &)
                ^~~
tmp_002-11,14,gcc,clang.cpp:7:29: note: only here as a friend
     { friend std::ostream & operator<< (std::ostream &, bar const &); };
                             ^~~~~~~~

I know that I can define the operator as follows

namespace foo
 {
   std::ostream & operator<< (std::ostream & o, bar const &)
    { return o; }
 }

but... what's wrong with my initial code ?

max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    This should help https://stackoverflow.com/questions/3891402/operator-overloading-and-namespaces – Garrigan Stafford Jun 25 '17 at 20:20
  • Have you found a solution yet or a comment why this is the case? also, why did you put friend up there in the first place? is this necessary to work? – Ilendir Aug 18 '17 at 22:52
  • @Ilendir - no "solution" found at the moment (but I don't search for a "solution"; I ask for an explanation); the first version of the (original, more complex) code isn't mine but is from an open source code; I've suggested to modify the code in the way showed in the second block (the operator inside a `namespace foo` block) but my doubt remain: why the first is wrong (or dangerous)? The answer from Robert.M is wrong. – max66 Aug 19 '17 at 11:29

4 Answers4

7

The answer of n.m. is right, although I needed a bit more digging to get it why, so here are some links that I followed:

The CppCoreGuidelines explain that "Nonmember operators should be either friends or defined in the same namespace as their operands". Here's a more detailed explanation of why the same namespace.
Maybe this this message from the GCC mailing list provides even more insight: it looks as if the GCC people decided to handle this issue more strictly some time im 2016.

The key here is the namespace. Your code would be OK if it defined the operator<< within the namespace foo instead of outside, like this:

namespace foo
{
    struct bar
    { 
        friend std::ostream & operator<< (std::ostream &, bar const &);
    };

    // Implementation
    std::ostream & operator<< (std::ostream & o, foo::bar const &)
    { return o; }
}

Note that this gets simpler, if you put the implementation directly with the friend definition, as shown in this SO answer to a similar question.

Here is another, quite elaborate SO answer to a very similar question. It helped me when I tried to fix the same problem (gcc 7 gave me this waring for code that compiled fine with much older versions).

Stefan
  • 81
  • 1
  • 5
6

Consider this simple program:

namespace xxx { 
    struct foo {
       friend void bar();
    };
}

int main() {
    xxx::bar();
}

You will get a compilation error (with clang too) because bar is not declared in namespace xxx.

Now consider this:

namespace xxx {}
void xxx::bar() {}

This will also fail for the same exact reason, bar is not declared in namespace xxx.

Now when you combine the two, there's no reason why the combination should suddenly become legal. bar is still not declared in namespace xxx. Yet clang permits it. This behaviour is inconsistent and confusing, and is best described as a bug.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
2

Well, I met the same problem. However I found a way to fix it: add another declaration of the function, within the namespace scope.

Just like this:

namespace foo {
    class A {
    public:
        friend std::ifstream &operator >> (std::ifstream &in, A &a);
    };

    std::ifstream &operator >> (std::ifstream &in, A &a);
}

std::ifstream &foo::operator >> (std::ifstream &in, A &a) {
    [...]
    return in;
}
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
hehe HE
  • 21
  • 1
0

I was using visual studio so I am not sure if it will solve a problem for you but the only thing I found wrong was a declaration of your operator. Basicly it is always

        const type_name variable_id &;

Ampersand always comes last and const comes first, for me it is running now.

You should change your operator declaration and to:

        friend std::ostream & operator<< (std::ostream &,const bar&);

And lower in your definition:

std::ostream & foo::operator<< (std::ostream & o,  const foo::bar &)
Robert.M
  • 1
  • 2
  • Thanks for the answer but no, you're wrong: the rule is that `const` is placed on the right of the part that we wont declare constant (by example: `int const *` declare a modifiable pointer to a constant integer where `int * const` declare a constant pointer to a modifiable integer) and only if there isn't nothing on the left of `const`, it is applied on the part in the right (so `const int *` is a modifiable pointer to a constant integer, exactly as `int const *`). So `const bar &` are exactly the same thing as `bar const &`. – max66 Jun 25 '17 at 20:31
  • But ... sorry... with visual studio you get the warning, compiling my code example, or nor? – max66 Jun 25 '17 at 20:33
  • Yes, 0 errors, 0 warnings, I forgot to mention that the same thing has to be done lower in the definition of the operator. – Robert.M Jun 25 '17 at 20:35
  • Thanks; another question: version of Visual Studio? – max66 Jun 25 '17 at 20:44
  • 2017 Community, starnge that it makes any difference. – Robert.M Jun 25 '17 at 20:48
  • I don't think the version of Visual Studio make difference but I insert your experience in my question; thanks again, – max66 Jun 25 '17 at 20:57