下面的代码无法使用gcc(9.2(和c++17进行编译。它确实可以与clang和MSVC一起工作,它也可以工作到c++14。发生了什么,谁是对的,有没有简单的解决方法?已决定使用#define
来消除gcc的unordered_set
过载,但我更喜欢"干净"的解决方案。
#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是正确的;这是因为在C++17模式中,它实现了DRP0522对CWG150的分辨率,允许在匹配模板模板参数时忽略默认模板参数:
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<T, Alloc>
与std::unordered_set<K, Hasher, Keyeq, Alloc>
(,因此重载是不明确的。注释中提到的解决方案是使泛型重载更具泛型,从而可以将std::unordered_set<K, Hasher, Keyeq, Alloc>
推导出它;例如:
template <typename T, class... Ts, template<typename, class...> class Container>
Stream & operator<< (Stream &stream, const Container<T, Ts...>& container)
这里std::unordered_set<K, Hasher, Keyeq, Alloc>
推导出Container<T, Ts...>
(具有Container = std::unordered_set
、T = K
、Ts = {Hasher, Keyeq, Alloc}
(,因此过载可以被部分排序。
这应该在C++14和C++17以及更高版本中都有效。