0

So I'm writing operator<< overload for a class as following:

#test.hh
#pragma once
#include<iostream>
namespace ns {
    struct test {
        double d;
        test(double x): d(x) {}
        friend std::ostream& operator<<(std::ostream&, const test&);
    };
}
#test.cc
#include"./test.hh"
std::ostream& operator<<(std::ostream& os, const ns::test& a) {
    os << a.d;
    return os;
}

Testing with following main:

#main.cc
#include"./test.hh"
#include<iostream>
int main() {
    ns::test a(12);
    std::cout << a << std::endl;
    return 0;
}

Which when compiled with g++ test.cc main.cc gave back error:

/usr/sbin/ld: /tmp/cc6Rs93V.o: in function `main':                                                      
main.cc:(.text+0x41): undefined reference to `ns::operator<<(std::ostream&, ns::test const&)'           
collect2: error: ld returned 1 exit status

Apparently the compiler resolves the function to ns::operator<< when it should be calling operator<<. I understand that C++ will find function within the namespace of argument, but did I implement the operator overloading incorrectly? Answers like this seems to have same implementation like me, which the only difference is they all wrote them in header.

Which part did I do wrong? And how could I fix such issue?

Andrew.Wolphoe
  • 390
  • 3
  • 16

2 Answers2

0

This is my solution for cases like this:

#include<iostream>

namespace ns 
{
    struct test 
    {
        double d;
        test(double x) : d(x) {}
        friend std::ostream& operator<<(std::ostream&, const test&);
    };

    std::ostream& operator<<(std::ostream& os, const ns::test& a) {
        os << a.d;
        return os;
    }
}

int main() 
{
    ns::test a(12);
    std::cout << a << std::endl;
    return 0;
}
Pepijn Kramer
  • 5,430
  • 2
  • 4
  • 16
  • Separating implementation from header is recommended (at least what I'm taught and currently preferred). It really have some impact when building large projects with autotools, which can save you plenty of compilation time – Andrew.Wolphoe Sep 18 '21 at 09:13
  • You understood correctly! :) Its just that stack overflow doesn't do seperate files very well. (I probably should have separated them out, just wanted to show the minimum amount of code) If you really want to understand more about separation of header files from code look at abstract base classes (and factories), dependency injection & the pimpl pattern :) Happy coding – Pepijn Kramer Sep 18 '21 at 09:21
0

The compiler uses the argument dependent lookup (ADL). As the second operand of the operator has the type specifier test declared in the name space ns then the compiler searches the operator also in the class definition of test and in the namespace ns where the class is declared. And such an operator is found because its declaration is present in the scope of the class test but the operator is not defined. So the compiler issues an error.

If you will define the operator then the compiler will issue another error that there are defined two operator and an ambiguity takes a place.

Vlad from Moscow
  • 265,791
  • 20
  • 170
  • 303