11

プライベートフレンド演算子を使用した通常のクラス(つまり、テンプレートではない)があります<<

宣言は次のとおりです。

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ファイルの先頭に定義を追加してfriend宣言を削除すると、正常に機能します...

これが私の主な機能での使用方法です

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}'lvalueを'std:: basic_ostream &&'</p>にバインドできません

std::coutステートメントと同じ行で発生しています。

だから、どうしたの...

前もって感謝します

4

2 に答える 2

16

私の推測では、説明から、あなたは2つを宣言しているが、operator<<1つだけを定義しているということです。例えば:

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を介して見つけることができる1つの関数を宣言していますBが、[2]はを宣言して定義しています::operator<<。コンパイラがコードを確認すると、次のようになります。

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

ADLを使用A::operator<<し、(フレンド宣言から)検索して使用しますが、その関数は未定義です。宣言を削除すると、グローバル名前空間に宣言および定義されfriendた単一のインスタンスがあり、それは通常のルックアップによって検出されます。operator<<

Aまた、プログラムにusingディレクティブがある場合、[2]の定義では引数の名前空間が明示的に指定されていない可能性があるため、これを見つけるのが難しい場合があることにも注意してください。これは、次のタイプの残りのメンバーを定義できることも説明します。

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

の定義B::fは(コード内で)グローバル名前空間にありますが、usingディレクティブがあるためBA名前空間にあり、型指定子は、解決後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]を見つけられない理由を尋ねて頭を悩ませてしまう可能性があります。

于 2012-05-23T02:13:44.740 に答える
1

デビッドはその質問にとても良い答えをしました。ここでは、簡単に理解できるように例を示しています。

私のヘッダーファイル:

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

上記は、名前空間外の演算子の宣言と実装であるため、独自のアプリケーションからこのライブラリにリンクしようとすると、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 <<(...、const BamTools :: SomeBamClass&)を使用すると、g++5.4.0のコンパイラエラーが発生します。

于 2016-12-13T23:58:34.113 に答える