【问题标题】:Unresolved external symbol for friend function未解析的友元函数外部符号
【发布时间】:2013-03-04 01:33:19
【问题描述】:

首先是我的代码布局:

啊.h

class STORAGE_CLASS_DECLARATOR A : public PureVirtual
{
   some member functions, all working;
   void someFunctionCallingOperator<<();
   friend std::ostream& operator << (std::ostream &, A *);
}

A.cpp

std::ostream& operator << (std::ostream & out, A * a){...}
void A::someFunctionCallingOperator<<(){...}

其中 Storage_xxx_declarators 是 declspec 的宏。我已经尝试过使用和不使用声明符的朋友功能都无济于事。

我在操作符

据我了解,这可能是由于:

  1. 没有函数体,这显然是不正确的,因为文件肯定会针对其他正在运行的函数进行解析。
  2. 符号未正确导出。同样,我不知道这是如何发生的,在实际场景中,此处 A 的 operator

我意识到链接是一个单独的阶段,我一定是在做一些非常简单的错误,但我已经盯着一段时间了,无法从理论上解释为什么会发生这种情况,所以我求助于 SO

谢谢你, AK

【问题讨论】:

  • 您遗漏了太多代码。你能准备一个minimal, but compilable example吗?
  • STORAGE_FUNC_DECLARATOR 宏的具体内容是什么?
  • @PLPiper 它是根据是否定义 Import/Export_storage_classes 有条件地定义的。目前它是导出(我正在构建 DLL),所以#define S_F_D __declspec(dllexport)
  • @Angew 我希望不会是这样,但我完全理解如果是这样。让我尝试添加更多细节。
  • 只是一个疯狂的猜测:您的类定义是否包含在命名空间中,并且 .cpp 包含 using namespace 指令?

标签: c++ dll linker unresolved-external


【解决方案1】:

根据您的 cmets,我假设您的设置是这样的:

啊.h

namespace NN {

class STORAGE_CLASS_DECLARATOR A : public PureVirtual
{
   some member functions, all working;
   void someFunctionCallingOperator<<();
   friend std::ostream& operator << (std::ostream &, A *);
}

}

A.cpp:

using namespace NN;

std::ostream& operator << (std::ostream & out, A * a){...}
void A::someFunctionCallingOperator<<(){...}

这是一个非常常见的设置,但我不鼓励任何人使用它,因为它会导致您现在遇到的问题。来看看A.cpp是怎么回事:

解析A::someFunctionCallingOperator的定义时,全局命名空间中没有A,所以考虑using-ed命名空间,将A解析为NN::A

然而,在解析operator&lt;&lt; 的定义时,没有任何迹象表明该运算符应该被放入命名空间NN。所以这很高兴地在全局命名空间中定义了operator&lt;&lt;,这绝不A.h 中的朋友声明所介绍的NN::operator&lt;&lt; 相关。后来,当你在someFunctionCallingOperator中使用&lt;&lt;时,通过参数依赖查找找到NN::operator&lt;&lt;(已在A.h中声明),因此使用它。最后,链接器正确地抱怨它从未被定义。

解决这个问题的正确方法是在A.cpp 中停止using namespace NN 并正确地将内容包含在命名空间中,就像在头文件中所做的那样:

namespace NN {

std::ostream& operator << (std::ostream & out, A * a){...}
void A::someFunctionCallingOperator<<(){...}

}

【讨论】:

  • 这几乎是完美的,但我似乎通过将 definition 更改为 std::ostream&amp; ::operator &lt;&lt; (std::ostream&amp; out, A * a) 以相反的方式解决了它(注意“操作员”前面的匿名命名空间. 这不是首选吗?我正在编写库,并且可以很好地控制它的设计方式,并且希望以正确的方式进行!
  • @AK4749 首先,全局命名空间(以:: 开头的命名空间)不是匿名命名空间。匿名命名空间 (namespace { ... }) 意味着别的东西。
  • @AK4749 其次,将运算符定义在与A 相同的命名空间中实际上是正确的做法,以便可以通过依赖于参数的查找找到它,但不会污染全局命名空间。这就是 stdstd::string 所做的,例如。
  • @AK4749 作为旁注:你不应该让操作员签名实际上是std::ostream&amp; operator&lt;&lt; (std::ostream&amp;, const A&amp;)吗?你得到它的方式,它用于流指向A的指针,它可以修改指向的A对象。
  • 不幸的是,签名是模块将要连接的旧代码的遗物,但非常感谢您的其他更正!从现在开始,我将确保使用正确的术语,我现在明白为什么“匿名命名空间”有两种不同的行为哈哈哈,在这种情况下,我会采纳你的建议并重构代码。我的困惑源于朋友声明,让我相信 operator
【解决方案2】:

简短回答:确保运算符的返回类型和参数在定义和实现中都是相同的。

答案稍长:

您似乎省略了运算符的返回类型。您的友元函数定义包括您的宏,但实现标识了ostream&amp; 的返回类型。如果ostream&amp; 未包含在您的宏中,这可能会导致未解决的外部符号错误。

您还省略了实现中的参数,如果您尝试将运算符与参数一起使用,这也会导致未解决的外部符号错误(这对于&lt;&lt; 来说是正常的)。编译器将寻找带有单个参数(和 ostream&amp; 返回类型)的 &lt;&lt; 的实现,但只会找到带有空参数列表的 operator&lt;&lt; 的实现。

【讨论】:

  • 感谢您的回答!我可以看到这将是一个问题。但是,正如问题中所述,我尝试使用 #define 存在而不是存在。它不存在,它目前是:cpp:std::ostream&amp; operator &lt;&lt; (std::ostream&amp; out, A * a) h:friend std::ostream&amp; operator &lt;&lt; (std::ostream&amp;, A *);
  • 除非您在定义中说,我应该再次使用 STORAGE_FUNC_DECLARATOR?
【解决方案3】:

我通过将方法放在同一个命名空间中解决了这个问题

【讨论】:

    猜你喜欢
    • 2016-06-19
    • 2014-04-20
    • 2013-11-03
    • 1970-01-01
    • 1970-01-01
    • 2015-11-17
    • 2011-02-09
    相关资源
    最近更新 更多