【问题标题】:c++ std::tuple of a variadic type list prefixc++ std::tuple of a variadic type list prefix
【发布时间】:2019-03-17 13:58:26
【问题描述】:

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

#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> >;
}

这在 gcc 8.2 上失败了:

错误:'struct prefix_tuple, double>'的模版实例化不明确

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

附:这在 clang 7.0 上也失败并出现类似错误,但似乎适用于 icc 19.0.1 和 msvc 19.16。

【问题讨论】:

  • 如果我添加专业化,gcc/clang 版本可以工作:template struct prefix_tuple,接下来,Ts...>{...};但是,加上这个,icc 无法编译。

标签: c++ metaprogramming


【解决方案1】:

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

这些是Partial Ordering 规则:

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

还有:

非正式地“AB 更专业”意味着“A 接受 B 类型的子集> 接受”。

AB 分别是我的代码中的第一个和第二个特化。 A 接受数字 n 大于 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...>;
};

C 不接受大于 0 的数字 n,也不接受前缀包后面的 0 类型的结构。因此,它是最专业的。另外,如果n==0C不能用,A也不能用,这样就解决了A之间的歧义B.

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

错误:多个部分特化匹配模板参数
类列表“prefix_tuple, 双>“:
"prefix_tuple, Tnext, Ts...>"
"prefix_tuple, Ts...>"

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

【讨论】:

    【解决方案2】:

    您也可以使用std::index_sequence:

    template <typename Seq, typename Tuple> struct prefix_tuple_impl;
    
    template <std::size_t ... Is, typename Tuple>
    struct prefix_tuple_impl<std::index_sequence<Is...>, Tuple>
    {
        using type = std::tuple<std::tuple_element_t<Is, Tuple>...>;
    };
    
    template <std::size_t N, typename ... Ts>
    using prefix_tuple_t = typename prefix_tuple_impl<std::make_index_sequence<N>,
                                                      std::tuple<Ts...>>::type;
    

    Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-12
      • 1970-01-01
      • 2017-12-03
      • 2023-04-10
      相关资源
      最近更新 更多