对运算符的未定义引用<<

问题描述 投票:0回答:3

我有一个常规课程(不是模板),有一个私人好友操作员<<

它的声明是:

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

它正在被编译,然后链接到使用它的主函数,但是当我使用它时,我得到一个未定义的引用......

但是,当我将定义添加到主 cpp 文件的顶部并删除朋友声明时,它工作正常......

这是我在主要功能中如何使用它的

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

不多也不少...

这是链接器错误

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

的未定义引用

这是类头...

#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}’左值绑定到‘std::basic_ostream&&’

它与 std::cout 语句发生在同一行...

所以,这是怎么回事...

提前致谢

c++ operators operator-overloading
3个回答
18
投票

我的猜测是,从描述来看,您声明了两个

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<<
函数,可以通过类型
B
上的 ADL 找到该函数,但 [2] 行声明并定义
::operator<<
。当编译器看到代码时:

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

它使用 ADL 并找到

A::operator<<
(来自友元声明)并使用它,但该函数未定义。如果删除
friend
声明,则会在全局命名空间中声明和定义
operator<<
的单个实例,并且可以通过常规查找找到该实例。

另请注意,如果程序中存在 using 指令,则可能更难发现这一点,因为 [2] 中的定义可能没有显式命名参数的

A
命名空间。这也解释了您可以定义该类型的其余成员:

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

B::f
的定义(在代码中)驻留在全局命名空间中,但由于使用指令,
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
投票

David 对这个问题给出了很好的回答。 这里我只是举个例子方便理解。

在我的头文件中:

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

上面将为您提供对运算符的未定义引用<<(.., const SomeBamClass&) when you try to link to this library from your own applications because it is declaration and implementation of an operator outside the namespace.

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<<(..., const BamTools::SomeBamClass&) will result in a compiler error for my g++ 5.4.0.


0
投票

您需要传递对ostream的引用;

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

© www.soinside.com 2019 - 2024. All rights reserved.