Специализация шаблона с const: ошибка "неоднозначного создания шаблона" на gcc, но не на clang - PullRequest
3 голосов
/ 28 мая 2019

Приведенный ниже код компилируется в clang (я пробовал 7 и 8), но не в gcc (7, 8 или 9), все с использованием --std=c++17.С gcc я получаю ошибку ambiguous template instantiation, как вы можете видеть здесь: https://godbolt.org/z/69Bomq.

Я могу исправить это для gcc, раскомментировав #define FIX_GCC.Если я изменяю first_type<C> и first_type<const C> на C и const C в специализациях traits<Map<...>>, он также компилируется, но в моем реальном коде, если есть некоторые enable_if_t<...>, чтобы сузить специализацию там.

GCC здесь не так?Или я делаю что-то не так, что clang может принять?

Обновление: Удаление одного слоя шаблонов, т.е. Map, в специализации также делает GCC счастливым даже с псевдонимом шаблонаfirst_type, см. https://godbolt.org/z/90kGEP.

<source>: In function 'int main()':
<source>:36:38: error: ambiguous template instantiation for 'struct traits<Map<const Foo> >'
   36 |   std::cout << traits<Map<const Foo>>::N << std::endl;
      |                                      ^~
<source>:24:8: note: candidates are: 'template<class C> struct traits<Map<first_type<C> > > [with C = const Foo]'
   24 | struct traits<Map<first_type<C>>> {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:30:8: note:                 'template<class C> struct traits<Map<first_type<const C> > > [with C = Foo]'
   30 | struct traits<Map<first_type<const C>>> {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:36:40: error: incomplete type 'traits<Map<const Foo> >' used in nested name specifier
   36 |   std::cout << traits<Map<const Foo>>::N << std::endl;
      |                                        ^
#include <iostream>
#include <type_traits>

template <class T, class...>
using first_type = T;

class Foo {};

template <class C>
class Map {};

template <class C>
struct traits {};

//#define FIX_GCC

#ifdef FIX_GCC
template <typename C>
struct traits<Map<first_type<C, std::enable_if_t<!std::is_const_v<C>>>>> {
  static constexpr int N = 2;
};
#else
template <typename C>
struct traits<Map<first_type<C>>> {
  static constexpr int N = 2;
};
#endif

template <typename C>
struct traits<Map<first_type<const C>>> {
  static constexpr int N = 3;
};

int main() {
  std::cout << traits<Map<Foo>>::N << std::endl;
  std::cout << traits<Map<const Foo>>::N << std::endl;

  return 0;
}
...