可变参数类型列表前缀的C ++ std :: tuple

Ben*_*y K 10 c++ metaprogramming

我正在尝试从可变参数类型列表中提取类型的前缀。这是我的尝试:

#include <tuple>
#include <type_traits>

template <typename... Ts>
struct pack{};

template <size_t n, typename... Args>
struct prefix_tuple;

template <size_t n, typename... TPrefix, typename Tnext, typename... Ts>
struct prefix_tuple<n, pack<TPrefix...>, Tnext, Ts...>{
    using type = typename 
        prefix_tuple<n-1, pack<TPrefix..., Tnext>, Ts...>::type;
};

template <typename... TPrefix, typename... Ts>
struct prefix_tuple<0, pack<TPrefix...>, Ts...>{
    using type = std::tuple<TPrefix...>;
};

template <size_t n, typename... Args>
using prefix_tuple_t = typename 
    prefix_tuple<n, pack<>, Args...>::type;

bool f(){
    return std::is_same_v<prefix_tuple_t<2, int, char, double>,
                          std::tuple<int, char> >;
}
Run Code Online (Sandbox Code Playgroud)

这在gcc 8.2上失败,原因是:

错误:“结构prefix_tuple <0,pack <int,char>,double>”的模板实例化不明确

第二个专业似乎比第一个专业更具体,所以我不明白为什么这里有歧义。我究竟做错了什么?

PS这在clang 7.0上也失败,并显示类似错误,但在icc 19.0.1和msvc 19.16上似乎可以使用。

Ben*_*y K 2

经过更多研究,以下是我的发现:

这些是部分排序规则:

1) 如果只有一种特化与模板参数匹配,则使用该特化。
2) 如果多个专业化匹配,则使用偏序规则来确定哪个专业化更专业。如果唯一,则使用最专业的特化(如果不唯一,则程序无法编译)
3) 如果没有专业匹配,则使用主模板

和:

通俗地说,“ AB更专业”意味着“ A接受B接受的类型的子集”。

AB分别为我的代码中的第一个和第二个专业化。 An接受数字大于 0的结构( B不接受)。另一方面,B接受前缀包后面带有 0 类型的结构(A不接受)。因此,AB都不是“最专业的”,并且该程序不应编译。也就是icc和msvc是错误的。


可能的解决方案:

假设我添加了我的评论中提到的以下第三个专业化(称为C):

template <typename... TPrefix, typename Tnext, typename... Ts>
struct prefix_tuple<0, pack<TPrefix...>, Tnext, Ts...>{
    using type = std::tuple<TPrefix...>;
};
Run Code Online (Sandbox Code Playgroud)

C不接受n大于 0 的数字,也不接受前缀包后面带有 0 类型的结构。因此它是最专业的。此外, ifn==0C不能使用,A也不能使用,因此这解决了AB之间的歧义。

添加此内容后,代码可以与 gcc、clang 和 msvc 一起使用,但 icc 拒绝它并出现以下错误:

错误:多个部分特化
与类“prefix_tuple<0UL, pack < int, char >, double>”的模板参数列表匹配:
“prefix_tuple<0UL, pack < TPrefix...>, Tnext, Ts...> "
"prefix_tuple<0UL, pack < TPrefix...>, Ts...>"

正如我之前提到的,其中第一个(C )比第二个( B )更专业,所以我必须再次推断出 icc 是错误的。