没有前向声明的嵌套函数模板实例化可以在 GCC 上编译,但不能在 clang 上编译
Posted
技术标签:
【中文标题】没有前向声明的嵌套函数模板实例化可以在 GCC 上编译,但不能在 clang 上编译【英文标题】:Nested function template instantiation without forward declaration compiles on GCC but not on clang 【发布时间】:2021-05-03 13:26:33 【问题描述】:以下内容不能在 clang 中编译,但在 GCC (godbolt) 中可以:
template <typename K, typename V>
std::ostream& operator<< (std::ostream& o, const std::map<K, V>& map)
const char* sep = "";
for (const auto& x : map)
o << sep << "" << x.first << ", " << x.second << "";
sep = ", ";
return o << "";
template <typename T>
std::ostream& operator<< (std::ostream& o, const std::vector<T>& vec)
const char* sep = "";
for (const auto& x : vec)
o << sep << x;
sep = ", ";
return o << "";
// Usage
int main()
std::map<int, std::vector<int>> t = 1, 2, 3, 4, 5;
std::cout << t << std::endl;
return 0;
谁是对的?
顺便说一句,我知道它是 UB,但是将两个模板定义放在 namespace std
中也会使代码在 clang 上编译。
代码借自Is it possible to define operator<< for templated types?
【问题讨论】:
如果你只是交换operator<<
重载的顺序,它应该可以编译。
@TedLyngmo 是的,但是如果涉及到地图的矢量,那么更改顺序会破坏它^^
@Fareanor 当然 - 在这种情况下,必须转发声明。 template <typename T> std::ostream& operator<<(std::ostream& o, const std::vector<T>& vec);
@TedLyngmo 同意,我说是因为在链接的问题中,OP 不想转发声明任何内容。
@Fareanor 啊,我明白了(我没有阅读借用代码的链接)。
【参考方案1】:
Clang 是对的。您应该将operator<<
对std::vector
的声明移到operator<<
对std::map
的定义之前。
在unqualified name lookup,
(强调我的)
对于模板定义中使用的依赖名称,查找会推迟到知道模板参数为止,此时 ADL 会检查从模板定义上下文以及模板实例化上下文中可见的函数声明
with external linkage (until C++11)
, 虽然非 ADL 查找仅检查从模板定义上下文可见的函数声明with external linkage (until C++11)
(换句话说,在模板定义之后添加新函数声明不会使其可见,除非通过 ADL) .
【讨论】:
我想知道造成这种差异的原因是什么 @Danra 关于 Clang 和 Gcc 之间区别的基本原理? ADL 和非 ADL 查找检查的声明之间 @Danra Non-ADL 在使用声明之前检查声明,这与非模板函数的工作方式相同。 ADL 检查声明用作参数的类的名称空间。在这种情况下,它们都找不到,因为std::vector
的operator<<
在std::map
的一个之后声明,那么它是不可见的,并且std::vector
在命名空间std
中声明,但operator<<
在全局命名空间中声明.以上是关于没有前向声明的嵌套函数模板实例化可以在 GCC 上编译,但不能在 clang 上编译的主要内容,如果未能解决你的问题,请参考以下文章