覆盖运算符<<时的友谊问题

发布于 2024-12-14 02:07:07 字数 1996 浏览 0 评论 0 原文

我正在尝试以标准方式重载 operator<< 。我有一个名为 SymbolTable 的类,位于名为 SymbolTable.h 的文件中,如下所示:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table);
private:
  vector<map<int, Symbol*> > mSymbols;
};

// ...

}

operator<< 的实现访问 SymbolTable 的一些私有成员。这是实现的签名,以证明它与前向声明的签名没有不同(我复制粘贴它以确保我不会发疯。)

ostream& operator<<(ostream &out, const SymbolTable &table)
{ ... }

如果我将实现放在 SymbolTable.h 中,其中第二...是,我收到以下链接器错误:

ld: /var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccstrYnU.o 中的重复符号 Compaler::operator<<(std::basic_ostream >&, Compaler::SymbolTable const&) 和/var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccDQFiyK.o 用于架构 x86_64

但是,如果我将其放入 SymbolTable.cpp(我的其余 SymbolTable 成员实现所在的位置)中,则代码将获胜甚至不编译;我收到与访问私有成员相关的错误,表明友谊未被识别:

../SymbolTable/SymbolTable.h:在函数 'std::ostream& 中运算符 <<(std::ostream&, const Compaler::SymbolTable&)': ../SymbolTable/SymbolTable.h:75:错误:'std::向量,std::分配器>,Compaler::Symbol*,std::less,std::分配器> >、std::分配器、std::分配器 >、Compaler::Symbol*> > >、std::分配器、std::分配器 >、Compaler::Symbol*、std::less、std::分配器 > >、std::分配器、std::分配器 >、Compaler::Symbol*> > > > > Compaler::SymbolTable::mSymbols' 是私有的 ../SymbolTable/SymbolTable.cpp:98: 错误:在此上下文中

我做错了什么?

答案摘要:

感谢 Seth Carnegie 和 Daniel R. Hicks,我已经弄清楚了。为了将来可能犯同样错误的人的利益,我将总结我的问题中的答案,因为评论中有很多来回。

第一个问题(当实现在 SymbolTable.h 中时出现重复符号)问题是因为我试图同时编译和链接包含 SymbolTable.h 的两个文件。我一直对 #ifdef include Guards 的工作方式存在误解;也就是说,它们只会阻止代码在一个文件中包含两次,但如果您尝试链接两个文件,而这两个文件都包含第三个文件中的代码,则它们不会对您有帮助。

第二个问题是,当我将 operator<< 实现放在 SymbolTable.cpp 文件中时,我忘记将它放在 Compaler 命名空间中。

I'm trying to overload operator<< in the standard way. I have a class called SymbolTable residing in a file called SymbolTable.h as follows:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table);
private:
  vector<map<int, Symbol*> > mSymbols;
};

// ...

}

The implementation of operator<< accesses some private members of SymbolTable. Here is the signature of the implementation, to demonstrate that it does not differ from the signature of the forward declaration (I copy-pasted it to be sure I wasn't going crazy.)

ostream& operator<<(ostream &out, const SymbolTable &table)
{ ... }

If I put the implementation in SymbolTable.h where the second ... is, I get the following linker error:

ld: duplicate symbol Compaler::operator<<(std::basic_ostream >&, Compaler::SymbolTable const&)in /var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccstrYnU.o and /var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccDQFiyK.o for architecture x86_64

However, if I instead put it in SymbolTable.cpp, where the rest of my SymbolTable member implementations are, the code won't even compile; I get errors related to accessing private members, indicating that the friendship is not being recognized:

../SymbolTable/SymbolTable.h: In function ‘std::ostream& operator<<(std::ostream&, const Compaler::SymbolTable&)’:
../SymbolTable/SymbolTable.h:75: error: ‘std::vector, std::allocator >, Compaler::Symbol*, std::less, std::allocator > >, std::allocator, std::allocator >, Compaler::Symbol*> > >, std::allocator, std::allocator >, Compaler::Symbol*, std::less, std::allocator > >, std::allocator, std::allocator >, Compaler::Symbol*> > > > > Compaler::SymbolTable::mSymbols’ is private
../SymbolTable/SymbolTable.cpp:98: error: within this context

What am I doing wrong?

Summary of answers:

Thanks to Seth Carnegie and Daniel R. Hicks, I've got it figured out. For the benefit of anyone who might make the same mistakes in the future, I'm going to summarize the answers in my question because there was a lot of back and forth in the comments.

The first (duplicate symbol when the implementation was in SymbolTable.h) problem occurred because I was trying to simultaneously compile and link two files which included SymbolTable.h . I have been labouring under a misunderstanding of how #ifdef include guards work; that is, they will only prevent code from being included twice in one file, but will not help you if you try to link two files which both included code from a third.

The second problem was that I forgot to put my operator<< implementation in the Compaler namespace when I had it in the SymbolTable.cpp file.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夏夜暖风 2024-12-21 02:07:07

如果您在头文件中执行此操作,则需要使用 inline:

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };

    inline ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}

如果您想在源文件中执行此操作。然后运算符<< 中声明

必须在命名空间Header.h

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };
    ostream& operator<<(ostream &out, const SymbolTable &table);
}

Source.cpp

#include "Header.h"
namespace Compaler   // must be here
{    
    ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}

If you do it in the header file you need to use inline:

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };

    inline ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}

If you want to do in the source file. Then the operator<< must be declared in the namespace

Header.h

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };
    ostream& operator<<(ostream &out, const SymbolTable &table);
}

Source.cpp

#include "Header.h"
namespace Compaler   // must be here
{    
    ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}
过潦 2024-12-21 02:07:07

第一个问题是因为您将定义放入头文件中并#include两次。这会导致多重定义错误。

至于第二个问题,您的函数可能具有与 friend 声明不同的签名。

只是为了让您知道,作为获得正确签名的替代方法,您可以内联编写函数:

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table) {
      ...
  }
private:
  vector<map<int, Symbol*> > mSymbols;
};

编辑:由于显然签名是正确的,因此错误位于其他地方。您必须发布更多代码。

The first problem is because you're putting the definition in the header file and #includeing it twice. This causes a multiple definition error.

As for the second problem, probably your function has a different signature than the friend declaration.

Just to let you know, as an alternative to getting the signature right, you can write the function inline:

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table) {
      ...
  }
private:
  vector<map<int, Symbol*> > mSymbols;
};

Edit: Since apparently the signature is right, the error lies somewhere else. You'll have to post more code.

陈年往事 2024-12-21 02:07:07

正如其他答案和评论所指出的那样,可以将其作为好友功能来执行。然而,我经常喜欢的另一种选择是创建一个公共打印函数,它将对象打印到 std::ostream,然后让operator<<调用该函数。这可以让您避免通过使操作符<<来破坏封装。好友功能:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  void print(ostream & out); // basically your definition of operator<<, in your .cpp file
private:
  vector<map<int, Symbol*> > mSymbols;
};

// this doesn't have to be inline, but since it's a two-liner, there's probably no harm either
inline ostream& operator<<(ostream &out, const SymbolTable &table)
{
    table.print(out);
    return out;
}

// ...

}

这也使您的客户可以选择避免 <<语法,这可能是好事,也可能不是好事。

另一个技巧:如果你对多个类执行此操作,你可以使operator<<模板,以便任何实现 print(ostream&) 的类型都可以输出到流:

template <typename T>
inline ostream& operator<<(ostream &out, const T &obj)
{
    obj.print(out);
    return out;
}

由于这是一个模板函数,因此类的任何特定定义都将覆盖此定义,而未定义 print(ostream&) 的类;) 只要它们从不输出到流中就可以,因为在这种情况下该函数永远不会被实例化。

As other answers and comments have pointed out, it is possible to do this as a friend function. However, an alternative I often prefer is to make a public print function, which prints the object to an std::ostream, and then let operator<< call that function. This lets you avoid breaking encapsulation by making operator<< a friend function:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  void print(ostream & out); // basically your definition of operator<<, in your .cpp file
private:
  vector<map<int, Symbol*> > mSymbols;
};

// this doesn't have to be inline, but since it's a two-liner, there's probably no harm either
inline ostream& operator<<(ostream &out, const SymbolTable &table)
{
    table.print(out);
    return out;
}

// ...

}

This also gives your clients the option to avoid the << syntax, which may or may not be a good thing.

Another trick: if you do this for multiple classes, you could make operator<< a template, so that any type implementing print(ostream&) can be output to a stream:

template <typename T>
inline ostream& operator<<(ostream &out, const T &obj)
{
    obj.print(out);
    return out;
}

Since this is a template function, any specific definitions of it for a class will override this definition, and classes that don't define print(ostream&) are fne as long as they are never output to a stream, since this function will never be instantiated in that case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文