c++operatorsoperator-overloading

undefined reference to operator<<


I have a regular class (not a template, that is) with a private friend operator<<

it's declaration is:

std::ostream& operator<<(std::ostream& out, const Position& position);

in the cpp file it's definition is:

std::ostream& operator<<(std::ostream& out, const Position& position)
{
  out << position.getXPoint() << "," << position.getYPoint();
  return out;
}

it is being compiled then linked to the main function which uses it, however when I use it I get an undefined reference...

however when I add the definition to the top of the main cpp file and remove the friend declaration, it works fine...

heres how I am using it in my main function

std::cout << node->getPosition() << std::endl;

no more no less...

heres the linker error

/home/luke/Desktop/pathfinder/parse_world.cpp:34: undefined reference to `pathfinder::operator<<(std::ostream&, pathfinder::Position const&)'

and heres the class header...

#ifndef PATHFINDER_H
#define PATHFINDER_H
#include <ostream>
#include <istream>
#include <list>
#include <vector>
#include <stdexcept>
#include <cstring>
namespace pathfinder
{
class Node;
typedef unsigned int GCost;
typedef std::vector<std::vector<Node*> > World;
typedef std::vector<Node*> WorldRow;
class Position
{
public:
    typedef unsigned int point_type;
private:
    point_type* xPoint_;
    point_type* yPoint_;
    friend std::ostream& operator<<(std::ostream& out, const Position& position);
public:
    Position(const point_type& xPoint = 0, const point_type& yPoint = 0);
    Position(const Position& position);
    Position(Position&& position);
    ~Position();

    Position& operator=(const Position& position);
    Position& operator=(Position&& position);

    point_type getXPoint() const;
    point_type getYPoint() const;

    void setXPoint(const point_type& xPoint);
    void setYPoint(const point_type& yPoint);
};
class Node
{
private:
    bool* isPassable_;
    bool* isStartingNode_;
    bool* isTargetNode_;
    Position* position_;
    GCost* additionalGCost_;
    Node* parent_;
public:
    Node(const bool& isPassable = true, const bool& isStartingNode = false, const bool& isTargetNode = false, const Position& position = Position(0,0), const GCost& additionalGCost = 0, Node* parent = nullptr);
    Node(const Node& node);
    Node(Node&& node);
    ~Node();

    Node& operator=(const Node& node);
    Node& operator=(Node&& node);

    bool isPassable() const;
    bool isStartingNode() const;
    bool isTargetNode() const;
    Position getPosition() const;
    GCost getAdditionalGCost() const;
    Node* getParent() const;

    void setAsPassable(const bool& isPassable);
    void setAsStartingNode(const bool& isStartingNode);
    void setAsTargetNode(const bool& isTargetNode);
    void setPosition(const Position& position);
    void setAdditionalGCost(const GCost& additionalGCost);
    void setParent(Node* parent);
};
class WorldHelper
{
public:
    static World fromStream(std::istream& input);
    static void syncPositions(World& world);
    static void uninitializeWorld(World& world);
    static Node* findStartingNode(const World& world);
    static Node* findTargetNode(const World& world);
};
class Pathfinder
{
public:
    virtual std::list<Node*> operator()(World& world) const = 0;
};
};
#endif //PATHFINDER_H

now after removing the friend declaration I am getting error messages like:

cannot bind ‘std::ostream {aka std::basic_ostream}’ lvalue to ‘std::basic_ostream&&’

it is occuring on the same line as the std::cout statement...

So, whats the deal...

thanks in advance


Solution

  • My guess, from the description is that you are declaring two operator<< but only defining one. For example:

    namespace A {
       struct B {
          friend std::ostream& operator<<( std::ostream&, B const & ); // [1]
       };
    }
    std::ostream& operator<<( std::ostream& o, A::B const & ) {        // [2]
       return o;
    }
    

    The line [1] declares one A::operator<< function that can be found through ADL on the type B, but [2] declares and defines ::operator<<. When the compiler sees the code:

    A::B b;
    std::cout << b;
    

    It use ADL and find A::operator<< (from the friend declaration) and use it, but that function is undefined. If you remove the friend declaration, there is a single instance of operator<< declared and defined in the global namespace and that will be found by regular lookup.

    Also note that this can be harder to spot if there are using directives in your program, as the definition in [2] might not explicitly name the A namespace for the argument. This would also explain that you can define the rest of the members of the type:

    // cpp [assume that the definition of B contained a member f
    using namespace A;
    void B::f() {
    }
    

    The definition of B::f resides (in code) in the global namespace, but because of the using directive, B will be found in the A namespace and the type specifier will be equivalent to A::B what makes the definition equivalent to void ::A::B::f() {} after resolving B. This will not happen for free functions.

    I would recommend that you avoid using directives as they allow for subtle errors like this one. Also note that you can actually define the operator in the namespace explicitly (but you will need to also declare it in the namespace:

    namespace A {
       struct B  { friend std::ostream& operator<<( std::ostream&, B const & ); };
       std::ostream& operator<<( std::ostream&, B const & );
    }
    std::ostream& A::operator<<( std::ostream& o, B const & ) {
       return o;
    }
    

    This trick (defining free functions outside of their natural namespace by fully qualifying) is sometimes use to avoid the definition of the function implicitly declaring it, which is prone to this type of errors. For example, if you defined the operator in the proper namespace, but the signature was slightly different:

    namespace A {
       struct B  {
          friend std::ostream& operator<<( std::ostream&, B const & ); // [1]
       };
       std::ostream& operator<<( std::ostream&, B & ) {                // [3]
          return o;
       }
    }
    

    The definition in [3] is also a declaration, but it declares a function different than the one declared in [1], and you might end up scratching your head asking why the linker is not finding [1].