Сбой разрешения перегрузки для g cc и c ++ 17 - PullRequest
2 голосов
/ 25 февраля 2020

Приведенный ниже код не компилируется с g cc (9.2) и c ++ 17. Он работает с clang и MSV C, он также работал до c ++ 14. Что происходит, кто прав и есть ли легкий обходной путь? Прибавил к #define перегрузку unordered_set для g cc, но я бы предпочел «чистое» решение.

#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

1 Ответ

4 голосов
/ 25 февраля 2020

г cc правильно; это связано с тем, что в режиме 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 и поэтому не видят перегрузку generi c как жизнеспособную.

При условии, что она жизнеспособна, и в соответствии с правилами для частичного упорядочения функции В шаблонах в C ++ 17 ни один соответствующий второй аргумент не может быть выведен из другого (Container<T, Alloc> против std::unordered_set<K, Hasher, Keyeq, Alloc>), поэтому перегрузки неоднозначны. Решение, которое упомянул в комментариях, состоит в том, чтобы сделать перегрузку generi c более обобщенной c, так что 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 и более поздних версиях.

...