【问题标题】:Undefined reference when defining a template function in the global namespace which is declared in an inline anonymous namespace在内联匿名命名空间中声明的全局命名空间中定义模板函数时未定义的引用
【发布时间】:2021-07-07 09:12:14
【问题描述】:

给定:

namespace ns
{
  inline namespace
  {
    template<typename T>
    void f();
  }
}

template<typename T>
void ns::f() {}

int main()
{
    ns::f<int>();
}

GCC (trunk) 抱怨 ns::f&lt;int&gt; 没有定义。 Clang(主干)对此很好。见:https://godbolt.org/z/n5qMs85q5

这是 GCC 中的一个已知错误吗? Clang 不正确吗?

【问题讨论】:

  • 我的解决方法是:namespace { inline namespace local { using namespace ns;模板 void f(); } } 模板 void local::f() {}

标签: c++ c++17 language-lawyer


【解决方案1】:

GCC 是对的,程序格式不正确。

inline 命名空间的成员可以做什么在 [namespace.def]/7 中指定:

内联命名空间的成员在大多数情况下都可以使用,就好像它们是封闭命名空间的成员一样。具体来说,内联命名空间及其封闭命名空间都会被添加到argument-dependent lookup 中使用的关联命名空间集合中,只要其中一个是,并且命名内联命名空间的using-directive 会隐式插入到封闭命名空间中,就像@ 987654324@。此外,内联命名空间的每个成员随后可以是partially specializedexplicitly instantiatedexplicitly specialized,就好像它是封闭命名空间的成员一样。最后,通过显式限定 ([namespace.qual]) 在封闭命名空间中查找名称将包括由 using-directive 引入的内联命名空间的成员,即使封闭命名空间中有该名称的声明。

因此,inline 命名空间成员可以被查找甚至专门化使用它的封闭范围名称,但不是定义

要定义一个成员,您仍然需要完全限定它。但是你不能限定一个未命名的命名空间。

要修复它,只需给它一个名字:

namespace ns
{
  inline namespace X
  {
    template<typename T>
    void f();
  }
}

template<typename T>
void ns::X::f()
{
  T t{};
  ++t;
}

奖金信息:

要了解未命名命名空间的工作原理,请参阅[namespace.unnamed]/1

unnamed-namespace-definition 的行为就好像它被替换为

inline(opt) namespace unique { /* empty body */ }
using namespace unique ;
namespace unique { namespace-body }

所以一个未命名的命名空间确实有一个名字,它只是对用户隐藏。因此,它永远不能完全有资格在其中定义东西(这实际上是重点)。

还有[namespace.memdef]/2:

命名命名空间的成员也可以通过显式限定在该命名空间外部定义...

这基本上意味着如果你可以限定它,你就可以定义它

【讨论】:

  • 感谢您的回答。我应该将此添加到原始问题中,但如果 f 不是模板函数,GCC 很乐意编译代码,例如:godbolt.org/z/sab7TTEYr。所以在那种特殊情况下,如果我理解正确的话,GCC 会错吗?
  • 在 C++ 中,如果某些东西可以编译,并不一定意味着它是正确的。请注意,错误来自链接器。我猜在常规函数的情况下,GCC 会产生相同的错误名称,最终会正确链接。但是给这个一些时间,也许其他人会发布另一个答案。
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 2019-09-21
  • 1970-01-01
  • 2013-08-16
  • 2020-12-03
  • 2020-02-19
  • 2015-05-10
  • 2017-05-31
相关资源
最近更新 更多