自动非类型模板参数:Clang中模棱两可的局部专业化

时间:2019-05-14 08:13:22

标签: c++ templates clang c++17 auto

Clang(7、8,trunk)拒绝以下代码

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的bug。 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 个答案:

答案 0 :(得分:1)

Clang演绎错误。它类似于this bug,并链接到此question(与您在模板参数中使用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 in template参数:

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