简体   繁体   English

gcc 和 c++17 的重载解析失败

[英]Overload resolution fails for gcc and c++17

The code below fails to compile with gcc (9.2) and c++17.下面的代码无法用 gcc (9.2) 和 c++17 编译。 It does work with clang and MSVC, it also worked up to c++14.它确实适用于 clang 和 MSVC,它也适用于 c++14。 What's going on, who is right and is there an easy workaround?这是怎么回事,谁是对的,是否有简单的解决方法? Resorted to #define ing the unordered_set overload away for gcc but I'd prefer a 'clean' solution.诉诸于#define ing 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;
}

Result:结果:

<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 https://godbolt.org/z/4dGu6L

gcc is correct; gcc 是正确的; this is because in C++17 mode it implements DR P0522 's resolution to CWG 150 , allowing default template parameters to be ignored when matching a template template parameter:这是因为在 C++17 模式下,它实现了DR P0522CWG 150的分辨率,允许在匹配模板模板参数时忽略默认模板参数:

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

The other compilers do not (yet) implement P0522 and therefore do not see the generic overload as viable.其他编译器(尚未)实现 P0522,因此不认为通用重载可行。

Given that it is viable, and under the rules for partial ordering of function templates in C++17, neither respective second argument can be deduced to the other ( Container<T, Alloc> vs. std::unordered_set<K, Hasher, Keyeq, Alloc> ) and so the overloads are ambiguous.鉴于它是可行的,并且在 C++17 中函数模板的偏序规则下,两个第二个参数都不能推导出另一个( Container<T, Alloc> vs. std::unordered_set<K, Hasher, Keyeq, Alloc> ) 等重载是不明确的。 The solution as mentioned in comments is to make the generic overload more generic, such that std::unordered_set<K, Hasher, Keyeq, Alloc> can be deduced to it;注释中提到的解决方案是使泛型重载更加泛型,这样std::unordered_set<K, Hasher, Keyeq, Alloc>就可以推导出来; for example :例如

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

Here std::unordered_set<K, Hasher, Keyeq, Alloc> deduces to Container<T, Ts...> (with Container = std::unordered_set , T = K , Ts = {Hasher, Keyeq, Alloc} ) and so the overloads can be partially ordered.这里std::unordered_set<K, Hasher, Keyeq, Alloc>推导出Container<T, Ts...> (使用Container = std::unordered_set , T = K , Ts = {Hasher, Keyeq, Alloc} )等等重载可以部分排序。

This should work in both C++14 and C++17, and later.这应该适用于 C++14 和 C++17 及更高版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM