【问题标题】:Is there a way to avoid the outside visibility of template friend declarations?有没有办法避免模板友元声明的外部可见性?
【发布时间】:2014-04-02 13:25:26
【问题描述】:

看看下面的例子:

啊哈:

class A {
protected:
  template<class T>
  friend void b();
};

b.h:

template<class T>
void b() {}

main.cxx:

#include "a.h"

int main()
{
  b<double>();
}

请注意,我忘记在主文件中包含“b.h”。不幸的是,编译器选择了类 A 中的朋友声明并编译了主函数而没有抱怨。我留下了一个神秘的链接错误,这令人惊讶,因为实例化应该发生在 main.cxx 中:

[joel@fedora-joel friend]$ g++ main.cxx 
/tmp/ccfpaeHw.o: In function `main':
main.cxx:(.text+0x5): undefined reference to `void b<double>()'
collect2: error: ld returned 1 exit status

我的问题是:为什么编译器允许这样做?有没有办法对付它?

【问题讨论】:

  • 根据单独编译的原则,只要一个TU所需的所有信息在编译时可用,每个翻译单元(TU)都会单独编译。对于带有 b 签名的函数,它肯定是(在预编译器将 a.h 传递到 main.cpp 之后)。未解析的引用被输入到符号表中,由链接器解析(因此出现链接器错误)。我不知道如何(或为什么)避免这种情况。
  • 为什么要避免这种情况?因为丑!恕我直言,将函数的存在作为类的朋友提及不应允许外部代码使用它。我确信这种行为有很好的技术原因,但是找到一种方法使该声明对外部代码不可用可以避免错误,尤其是对于初学者。
  • “为什么”更多是技术问题。如果您通过编译器/链接器所采取的步骤,这将很难实现(例如,您需要一些跨 TU 有效性检查)。 C(++) 传统上使用“单独编译”,这在当时是不可能的。是的,我同意,这可能会在调试时导致难以跟踪的错误。
  • 这就是为什么你不应该向在不同标题中定义的东西声明友谊。 protected: 在这里也毫无意义;朋友声明没有访问说明符。

标签: c++ templates


【解决方案1】:

ISO/IEC 14882:2003,§11.4,第 3 段是这样说的:

在友元声明中首次声明的函数具有外部链接 (3.5)。否则,该函数将保留其先前的链接 (7.1.1)。

因此,朋友声明兼作函数声明。这样main就可以调用函数,所以不会出现编译错误。

链接器错误是因为声明没有匹配的函数定义。

我没有看到避免这种行为的方法(因为标准规定了它)。这也是有道理的,因为无论如何都需要一个函数声明来使友元声明有效(否则你会有一个未声明函数的友元声明)。因此,如果朋友声明不能兼作函数声明,则必须以其他方式提供函数声明(例如通过包含适当的标头)。

【讨论】:

  • 感谢您引用标准,它总是很有帮助。但是,您还没有解决我问题的第二部分。我想你的答案是否定的?
  • @static_rtti :是的 - 应该明确添加 - 现在会解决这个问题。
猜你喜欢
  • 2013-06-27
  • 2021-04-11
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 2013-09-18
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多