带有自动模板参数的部分模板特化

Partial template specialization with auto template argument

我有两个枚举

#include <iostream>

enum class E1 : unsigned int
{
    E11 = 1
};

enum class E2 : unsigned int
{
    E21 = 1
};

在这种情况下具有相同的基础值 (1)。接下来,我有一个 class C,它有两个模板参数,一个整数 j 和一个类型为 auto.

的值 i
template<int j, auto i>
struct C
{ C() { std::cout << "none\n"; } };

我想为 E1::E11E2::E21 部分专门化此 class,如下所示:

template<int j>
struct C<j, E1::E11>
{ C() { std::cout << j << "E11\n"; } };

template<int j>
struct C<j, E2::E21>
{ C() { std::cout << j << "E21\n"; } };

为了完整起见,这里主要是实例化两个对象:

int main()
{
    C<0,E1::E11> e11;
    C<1,E2::E21> e21;
    return 0;
}

上面的代码在 gcc 和 icc 上工作得很好,可以在 Godbolt (full code)

上验证

但它在任何其他编译器(clang、msvc)上都失败了。带有以下消息

<source>:27:18: error: ambiguous partial specializations of 'C<0, E1::E11>'
    C<0,E1::E11> e11;
                 ^
<source>:18:8: note: partial specialization matches [with j = 0]
struct C<j, E1::E11>
       ^
<source>:22:8: note: partial specialization matches [with j = 0]
struct C<j, E2::E21>

我很清楚为什么会这样。我似乎无法回答的问题是是否有可能以标准兼容的方式解决这个问题(如果这是一些 gcc 或 icc 功能)或者是否有针对失败的编译器(clang,msvc)的解决方法。

顺便说一句。如果我删除 int j 模板参数并只保留 auto i 代码将与所有编译器一起编译。

提前致谢,

阿尔诺

auto 模板参数可以替换为

template<int j,typename T,T i>
struct C
{ C() { std::cout << "none\n"; } };

如果您愿意输入更多内容,您可以明确指定枚举的类型:

#include <iostream>

enum class E1 : unsigned int
{
    E11 = 1
};

enum class E2 : unsigned int
{
    E21 = 1
};


template<int j,typename T,T i>
struct C
{ C() { std::cout << "none\n"; } };


template<int j>
struct C<j,E1, E1::E11>
{ C() { std::cout << j << "E11\n"; } };

template<int j>
struct C<j,E2, E2::E21>
{ C() { std::cout << j << "E21\n"; } };


int main()
{
    C<0,E1,E1::E11> e11;
    C<1,E2,E2::E21> e21;
    return 0;
}

为了减少打字,您可以使用辅助函数:

template <int j,auto i>
auto make_C(){
    return C<j,decltype(i),i>();
}

int main()
{
    auto e11 = make_C<0,E1::E11>();
    auto e21 = make_C<1,E2::E21>();
}

... 或类型特征:

template <int j,auto i>
struct C_helper {
    using type = C<j,decltype(i),i>;
};

int main()
{
    C_helper<0,E1::E11>::type e11;
    C_helper<1,E2::E21>::type e21;
}

msvc/clang 在您的案例中似乎存在部分专业化问题:(

作为解决方法,您可以将参数拆分为 2 类:


template <auto i>
struct EC
{
    template <int j>
    struct C
    {
        C() { std::cout << "none\n"; }
    };
};

template <>
struct EC<E1::E11>
{
    template <int j>
    struct C
    {
        C() { std::cout << j << "E11\n"; }
    };
};

// Same for E2::E21

然后

EC<E1::E11>::C<0> e11;
EC<E2::E21>::C<0> e21;

Demo