Undefined ссылка на оператор <<

У меня есть обычный класс (а не шаблон, то есть) с оператором частного друга < <

это объявление:

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

в файле cpp это определение:

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

он компилируется, затем привязывается к основной функции, которая его использует, однако, когда я его использую, я получаю ссылку undefined...

однако, когда я добавляю определение в верхнюю часть основного файла cpp и удаляю объявление друга, он отлично работает...

heres, как я использую его в своей основной функции

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

не больше не менее...

heres ошибка компоновщика

/home/luke/Desktop/pathfinder/parse_world.cpp:34: undefined ссылка на `pathfinder:: operator < < (std:: ostream &, pathfinder:: Position const &) '

и heres заголовок класса...

#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

теперь после удаления объявления друга я получаю сообщения об ошибках, например:

не может связывать 'std:: ostream {aka std:: basic_ostream} lvalue с' std:: basic_ostream &&

происходит в той же строке, что и инструкция std:: cout...

Итак, в чем дело...

заблаговременно

Ответ 1

Мое предположение, из описания состоит в том, что вы объявляете два operator<<, но только определяете их. Например:

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;
}

Строка [1] объявляет одну функцию A::operator<<, которая может быть найдена через ADL в типе B, но [2] объявляет и определяет ::operator<<. Когда компилятор видит код:

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

Он использует ADL и находит A::operator<< (из объявления друга) и использует его, но эта функция undefined. Если вы удалите объявление friend, в глобальном пространстве имен есть один экземпляр operator<<, объявленный и определенный, который будет найден путем регулярного поиска.

Также обратите внимание, что это может быть труднее обнаружить, если в вашей программе есть директивы, так как определение в [2] может явно не указывать пространство имен A для аргумента. Это также объясняет, что вы можете определить остальных членов типа:

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

Определение B::f сохраняется (в коде) в глобальном пространстве имен, но из-за директивы use B будет найдено в пространстве имен A, а спецификатор типа будет эквивалентен A::B, что делает определение эквивалентным void ::A::B::f() {} после разрешения B. Это будет не для бесплатных функций.

Я бы рекомендовал вам избегать использования директив, поскольку они допускают такие тонкие ошибки, как эта. Также обратите внимание, что вы можете явно определить оператор в пространстве имен явно (но вам также нужно объявить его в пространстве имен:

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;
}

Этот трюк (определение свободных функций вне их естественного пространства имен путем полной квалификации) иногда используется, чтобы избежать определения функции, неявно объявляющей ее, которая подвержена этому типу ошибок. Например, если вы определили оператор в собственном пространстве имен, но подпись была несколько иной:

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

Определение в [3] также является объявлением, но оно объявляет функцию, отличную от той, которая заявлена ​​в [1], и вы можете почесать голову, спрашивая, почему компоновщик не находит [1].

Ответ 2

Дэвид дал очень хороший ответ на этот вопрос. Здесь я просто предоставляю пример для легкого понимания.

в моем файле заголовка:

namespace BamTools {
class SomeBamClass {
.....
public:
    friend std::ostream& operator<<(std::ostream& ous, const SomeBamClass& ba);
    .....
}
}

В файле cpp:

using namespace BamTools; // this work for Class member function but not friends
using namespace std;

........
std::ostream& operator<<(std::ostream &ous, const SomeBamClass &ba) {
  // actual implementation
  return ous;
}

Вышеупомянутое даст вам undefined ссылку на operator < (.., const SomeBamClass &), когда вы пытаетесь связать эту библиотеку с вашими собственными приложениями, поскольку это объявление и реализация оператора вне пространства имен.

undefined reference to `BamTools::operator<<(std::ostream&, BamTools::SomeBamClass const&)'

Единственное, что работает для меня - это добавить включение пространства имен в функцию друга в файле реализации:

namespace BamTools {
std::ostream& operator<<(std::ostream &ous, const SomeBamClass &ba) {
...
   return ous;
}
}

используя... BamTools:: operator < (lt; (..., const BamTools:: SomeBamClass &) приведет к ошибке компилятора для моего g++ 5.4.0.