【问题标题】:Overload resolution fails for gcc and c++17gcc 和 c++17 的重载解析失败
【发布时间】:2020-02-25 09:03:26
【问题描述】:

以下代码无法使用 gcc (9.2) 和 c++17 编译。它确实适用于 clang 和 MSVC,它也适用于 c++14。发生了什么事,谁是对的,是否有简单的解决方法?诉诸 #defineing unordered_set 重载 gcc 但我更喜欢“干净”的解决方案。

#include <unordered_set>
#include <iostream>

struct Stream
{};

template <typename T, typename Alloc, template<typename, typename> class Container>
Stream & operator<< (Stream &stream, const Container<T, Alloc>& container)
{
  std::cout << "container" << std::endl;
  return stream;
}

template <class K, class Hasher, class Keyeq, class Alloc>
Stream & operator<< (Stream &stream, const std::unordered_set<K, Hasher, Keyeq, Alloc> & container)
{
  std::cout << "unordered_set" << std::endl;
  return stream;
}

int main()
{
    Stream t;
    std::unordered_set<int> set;
    t << set;

    return 0;
}

结果:

<source>: In function 'int main()':    
<source>:25:7: error: ambiguous overload for 'operator<<' (operand types are 'Stream' and 'std::unordered_set<int>')    
   25 |     t << set;    
      |     ~ ^~ ~~~    
      |     |    |    
      |     |    std::unordered_set<int>    
      |     Stream    
<source>:8:10: note: candidate: 'Stream& operator<<(Stream&, const Container<T, Alloc>&) [with T = int; Alloc = std::hash<int>; Container = std::unordered_set]'    
    8 | Stream & operator<< (Stream &stream, const Container<T, Alloc>& container)    
      |          ^~~~~~~~    
<source>:15:10: note: candidate: 'Stream& operator<<(Stream&, const std::unordered_set<_Value1, _Hash1, _Pred1, _Alloc1>&) [with K = int; Hasher = std::hash<int>; Keyeq = std::equal_to<int>; Alloc = std::allocator<int>]'    
   15 | Stream & operator<< (Stream &stream, const std::unordered_set<K, Hasher, Keyeq, Alloc> & container)    
      |          ^~~~~~~~    
ASM generation compiler returned: 1    
<source>: In function 'int main()':    
<source>:25:7: error: ambiguous overload for 'operator<<' (operand types are 'Stream' and 'std::unordered_set<int>')    
   25 |     t << set;    
      |     ~ ^~ ~~~    
      |     |    |    
      |     |    std::unordered_set<int>    
      |     Stream    
<source>:8:10: note: candidate: 'Stream& operator<<(Stream&, const Container<T, Alloc>&) [with T = int; Alloc = std::hash<int>; Container = std::unordered_set]'    
    8 | Stream & operator<< (Stream &stream, const Container<T, Alloc>& container)    
      |          ^~~~~~~~    
<source>:15:10: note: candidate: 'Stream& operator<<(Stream&, const std::unordered_set<_Value1, _Hash1, _Pred1, _Alloc1>&) [with K = int; Hasher = std::hash<int>; Keyeq = std::equal_to<int>; Alloc = std::allocator<int>]'    
   15 | Stream & operator<< (Stream &stream, const std::unordered_set<K, Hasher, Keyeq, Alloc> & container)    
      |          ^~~~~~~~    
Execution build compiler returned: 1

https://godbolt.org/z/4dGu6L

【问题讨论】:

  • 请在 gcc 的 bugzilla 上报告(或查找现有报告)并在您的问题中提供链接。
  • 在第一次重载中将 T,Alloc 对替换为 T... 似乎有帮助。

标签: c++ gcc c++17


【解决方案1】:

gcc 是正确的;这是因为在 C++17 模式下它实现了DR P0522 对CWG 150 的解析,允许在匹配模板模板参数时忽略默认模板参数:

template <template <typename> class> void FD();
template <typename, typename = int> struct SD { /* ... */ };
FD<SD>();  // OK; error before this paper (CWG 150)

其他编译器(尚未)实现 P0522,因此不认为泛型重载是可行的。

鉴于它是可行的,并且根据 C++17 中函数模板的部分排序规则,不能将各自的第二个参数推导出另一个(Container&lt;T, Alloc&gt; 与 std::unordered_set&lt;K, Hasher, Keyeq, Alloc&gt;),因此重载是模糊的。 cmets中mentioned的解决方案是让泛型重载更通用,这样std::unordered_set&lt;K, Hasher, Keyeq, Alloc&gt;就可以推导出来; for example:

template <typename T, class... Ts, template<typename, class...> class Container>
Stream & operator<< (Stream &stream, const Container<T, Ts...>& container)

这里std::unordered_set&lt;K, Hasher, Keyeq, Alloc&gt; 推导出Container&lt;T, Ts...&gt;(带有Container = std::unordered_set、T = K、Ts = {Hasher, Keyeq, Alloc}),因此重载可以部分排序。

这应该适用于 C++14 和 C++17 及更高版本。

【讨论】:

  • 难以置信的答案,非常感谢! (也感谢@Marc)。 (yet) 部分有点担心哦
猜你喜欢
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多