Why I cannot put this operator overload in the same namespace as the struct?
Asked Answered
R

3

6

I have the following code:

#include <iostream>
#include <vector>
namespace X {
    std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
        for (int i=0;i<v.size();i++){os << v[i] << " ";}
        return os;
    }
    namespace Y {
        struct A {std::vector<double> x;};    
        std::ostream& operator<<(std::ostream& os,const A& a){
            os << a.x << std::endl;
            return os;
        }
   }     
}

using namespace X;

int main(int argc, char** argv) {
   std::vector<double> v(10,0);
   std::cout << v << std::endl;
   Y::A a;
   std::cout << a << std::endl;
   return 0;
}

The first overload works, but the second one does not. For some reason it cannot find the first one. I get the error:

no match for 'operator<<' (operand types are 'std::ostream 
{aka std::basic_ostream<char>}' and 'const std::vector<double>')
     os << a.x << std::endl;
        ^

I do not understand why I get this error. For example something like this seems to be completely valid:

namespace A {
    void foo(){}
    namespace B {
        void bar(){foo();}
    }
}

However, the only way to fix the above problem was to put the second overload also in X. Why is it not possible to have it in the same namespace as the struct (ie. X::Y)?

PS: I was reading on ADL and I found some related questions (e.g. this and this, but what I understood from reading this, the above should work.

Ropable answered 6/5, 2015 at 15:0 Comment(3)
just in case it matters, I am using g++ 4.9.1Ropable
You need a using X::operator<< in namespace Y.Osteoblast
@0x499602D2 Yes, that works and is much nicer than moving the overload to X. Still it would be nice to know why ADL does not apply in this case.Ropable
D
2

In Argument Depended Lookup (or Koenig Lookup), compiler adds to the scope of visibility all symbols declared in parent scopes of each parameter.

Even if Y is "child namespace" of X, they are not related in terms of ADL. First of your parameters is type defined in std:: namespace, while second is local symbol (defined in the same namespace as the function itself).

Note, that because of reasons mentioned above, you will most likely get another error in this line:

std::cout << v << std::endl;

when compiler will not be able to find operator<< overloaded for std::vector<double> (because it lies inside namespace X).

To solve this, you can use:

using X::operator<<

inside namespace Y or move that overload.

If you are wondering, why foobar example works: that's because ADL (Argument Dependent Lookup) is about scope of parameters of functions, not functions themselves. In foobar code, ADL isn't applied.

Debora answered 6/5, 2015 at 15:31 Comment(2)
Nice explanation. I didnt get the error for the vector because of using namespace X. I guess then the answer to where to put the overloads of << is different from what was answered here. Should I put them in global namespace or even in std?Ropable
I would put them in global namespace, using fully-qualified types of parameters (std::vector and X::a).Debora
C
2

As per other answers, I eventually deduced that ADL of operator<< was being impeded by the fact that it was taking place inside another operator<<.

Today's lesson: always write an operator<< overload in terms of a writer method :)

Here's the fix:

#include <iostream>
#include <vector>


namespace X 
{
    std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
        for (int i=0;i<v.size();i++){os << v[i] << " ";}
        return os;
    }
    namespace Y 
    {
        struct A 
        { 
            std::vector<double> x;
            void write(std::ostream&os) const {
                os << x << std::endl;
            }
        };    
        std::ostream& operator<<(std::ostream& os,const A& a)
        {
            a.write(os);
            return os;
        }
    }     
}

using namespace X;

int main(int argc, char** argv) 
{
   std::vector<double> v(10,0);
   std::cout << v << std::endl;
   X::Y::A a;
   std::cout << a << std::endl;
   return 0;
}
Cosmism answered 6/5, 2015 at 15:18 Comment(5)
Then why is the foobar example ok? Btw I did not expect the using namespace X to have any influence on the problem. I just put it to have access to X in the main.Ropable
I mean namespace Y is declared inside namespace X, just as B is inside A and thus bar can call fooRopable
looks reasonable, however I would like to avoid using some writer method. Also, in my real problem, it really would be much more comfortable to use already existing "<<" of components to implement the "<<" of a bigger struct/class.Ropable
sorry didnt read your code carfully enough. You are using the vectors << to implement the structs <<. Actually now I am a bit confused ;) I will have to take another look when I have time.Ropable
@tobi303 no, my code is essentially the same as yours except X::Y::operator<< defers to X::Y::A::write rather than trying to call X::operator<< directly (because that is being hidden by the enclosing function's name). Calling operator<< from an operator<< when relying on ADL is a no-go. You can defer to a free function if you prefer, but you can't lookup the same name as yourself.Cosmism
D
2

In Argument Depended Lookup (or Koenig Lookup), compiler adds to the scope of visibility all symbols declared in parent scopes of each parameter.

Even if Y is "child namespace" of X, they are not related in terms of ADL. First of your parameters is type defined in std:: namespace, while second is local symbol (defined in the same namespace as the function itself).

Note, that because of reasons mentioned above, you will most likely get another error in this line:

std::cout << v << std::endl;

when compiler will not be able to find operator<< overloaded for std::vector<double> (because it lies inside namespace X).

To solve this, you can use:

using X::operator<<

inside namespace Y or move that overload.

If you are wondering, why foobar example works: that's because ADL (Argument Dependent Lookup) is about scope of parameters of functions, not functions themselves. In foobar code, ADL isn't applied.

Debora answered 6/5, 2015 at 15:31 Comment(2)
Nice explanation. I didnt get the error for the vector because of using namespace X. I guess then the answer to where to put the overloads of << is different from what was answered here. Should I put them in global namespace or even in std?Ropable
I would put them in global namespace, using fully-qualified types of parameters (std::vector and X::a).Debora
B
1

As simple as this: In order to overload a function the overloaded version have to lives in the same nemaspace, otherwise, is a completely different function. The name of the function (for the compiler) is the complete path from the global namespace to the function itself.

::function_at_global_namespace();
Namespace::function_name();      // Some funtion within a namespace;
Namespace_1::function_name();    // Some other function within another namespace;

So,

Standar std::ostream& operator<< lives in std namespace, you are not overloading that operator, Just defining anotherone in namespace X.

As pointed by @0x499602D2 you must use X::operator<< in namespace Y in order to call that version of the operator.

std::ostream& std::operator<< and std::ostream& X::operator<< are diferent functions.

In following code none of the foo versions are overloading either.

// What version of foo gets called?  A::foo, or B::foo?
namespace A {
    void foo(){cout << "A::foo" << endl;}
    namespace B {
        void foo(){ cout << "B::foo" << endl;}
        void bar(){foo();}
    }
}

namespace C { void foo(int a) { cout << "C:foo" << endl; } }
Battista answered 6/5, 2015 at 15:28 Comment(2)
good point. But then I do not really understand why ADL does not apply in this case.Ropable
From main(), it is not ADL that resolves the first use of operator<<, but the fact you said using namespace X. The second use of operator<< in main() resolves to X::Ys version due to ADL. From X::Y, there is no using, so it is using ADL. But, the arguments are in the std namespace, so that is where ADL looks.Spandrel

© 2022 - 2024 — McMap. All rights reserved.