Параметр шаблона автоматического нетипичного типа: неоднозначные частичные специализации в Clang - PullRequest
10 голосов
/ 14 мая 2019

Clang (7, 8, транк) отклоняет следующий код

enum class E {};
inline static constexpr auto e = E{};
// inline static constexpr auto e = nullptr;

template<auto, int> class S;
template<int a, int b> class S<a, b> {};
template<int b> class S<e, b> {};

int main() {
    S<0, 0> s;
}

с ошибкой:

error: ambiguous partial specializations of 'S<0, 0>'
note: partial specialization matches [with a = 0, b = 0]
template<int a, int b> class S<a, b> {};
                             ^
note: partial specialization matches [with b = 0]
template<int b> class S<e, b> {};
                      ^
  1. Почему это неоднозначно? Как e может соответствовать 0? Если я заменю E{} на nullptr, Clang перестанет жаловаться. Это похоже на ошибку Clang. GCC компилирует это просто отлично.

  2. Если это ошибка, какой обходной путь? В моем случае параметр auto может быть либо E (и только одно значение E{}), либо int. Тогда:

    template<auto, int, typename> class S_impl;
    template<int a, int b> class S_impl<a, b, int> {};
    template<int b> class S_impl<e, b, E> {};
    
    template<auto a, int b> using S = S_impl<a, b, decltype(a)>;
    

    Есть ли более лаконичный способ?

1 Ответ

1 голос
/ 14 мая 2019

Clang делает вывод неправильно. Это похоже на эту ошибку , связанную с этим вопросом (не совсем то же самое, что вы используете auto в параметрах шаблона, что помешает вам скомпилировать с использованием stdc ++ 14).

Интересный случай - это не тот случай, если это полная специализация; только по частичной специализации:

#include <iostream>

enum class E {};
inline static constexpr auto e = E{};

template <auto a, int b>
class FOO;
template <int a, int b > class FOO<a, b> {};
template <int b> class FOO<e, b> {};

template <auto a, int b>
class BAR;
template <int a, int b > class BAR<a, b> {};
template <> class BAR<e, 0> {};

template <auto a>
class BAZ;
template <int a> class BAZ<a> {};
template <> class BAZ<e> {};

int main() {
    // FOO <0, 0> foo; // <= Not Ok
    BAR<0, 0> bar; // <= Ok
    BAZ<0> baz; // <= Ok
}

Любое решение, которое вынуждает удерживать параметр шаблона типа, будет работать, поэтому предложенное вами решение является абсолютно верным. ИМХО, я бы не использовал auto в параметре шаблона, когда нет необходимости улучшать читабельность:

template <typename T, T value, int> class S_impl; // <= this auto is not necessary
template <int a, int b> class S_impl<int, a, b> {};
template <int b> class S_impl<E, e, b> {};
// Either define S to use S<0,0> or directly use S_impl<int, 0, 0>
template <auto a, int b> using S = S_impl<decltype(a), a, b> // <= this auto is necessary
...