递归折叠参数包以解析占位符类型

时间:2018-02-24 20:21:54

标签: c++ c++11 templates variadic-templates c++17

注意:跟进this question

在询问this question about parameter pack folding into pairs之后,我注意到我需要保留以前折叠类型的完整类型作为左对类型。

例如:

Fold<char, int, long, double> f;

必须评估为

std::tuple<
    std::pair<char
             , int>,
    std::pair<std::pair<char, int> /* <-- the previous resulting type */
             , long>,
    std::pair<std::pair<std::pair<char, int>, long> /* the previous type again */
             , double>
> f;

此问题的上下文

我之所以需要这个,是因为必须折叠的类型&#34;可以是占位符类型。占位符&#34;真实&#34;只有当左侧具有完全展开类型以及未展开类型时,才能知道类型。 最左边的类型从不包含占位符。

让我用一个简单的例子来说明这一点:

struct CopyTypeFromPreviousArgumentTag { };

template<typename T = CopyTypeFromPreviousArgumentTag>
struct Foo;

template<typename T...>
struct Bar {
    /* Here fold will not use std::pair, but a resolver type that takes both A and B and gives back the resolved B */
    Fold<T...> expanded;
};

现在Bar可以像这样使用:

Bar< Foo<int>
   , Foo<>
   , Foo<>
   , Foo<double>
   , Foo<>
   > f;

,内部类型decltype(f::expanded)将为:

std::tuple< Foo<int>
          , Foo<int>
          , Foo<int>
          , Foo<double>
          , Foo<double>
          >;

编辑: Bar类实际上不限于它可能包含的任何类类型。它可以是几种类型的混合物。因此,请将Foo类视为某种类型Foo的占位符,其中存在解析器类型特征,并且先前已解析类型:ResolveType<PreviouslyResolvedType, CurrentType>::Type将正确地提供已解析的类型。因此std::pair成语。

我目前的尝试

我试图通过建立链接问题的答案来实现递归,但无法使其起作用。

namespace Detail {

template<typename, typename...>
struct Fold;

template
    < size_t... Indices
    , typename... Types
> struct Fold<std::index_sequence<Indices...>, Types...> {
    using Tuple = std::tuple<Types...>;
    using Type = std::tuple<std::pair /* use std::pair just to match the first example */
        //< std::tuple_element_t<Indices, Tuple>
        < typename Fold
            < std::tuple_element_t<Indices, Tuple>
            , std::make_index_sequence<Indices>
            , Types...>::Type; /* Tuple can't be expanded :( */
        , std::tuple_element_t<Indices + 1, Tuple>
        >::Type...>;
};

} /* namespace Detail */


template<typename... Types>
using Fold = typename Detail::Fold<std::make_index_sequence<sizeof...(Types) - 1>, Types...>::Type;

2 个答案:

答案 0 :(得分:4)

链接的问题是一种非常复杂的方式。如果它是一个运行时问题,它显然可以用一次通过算法解决,元编程也没有什么不同。

struct copy_prev {};

template<typename T = copy_prev>
struct Foo {};

template<typename... Ts, typename T>
auto operator+(std::tuple<Ts...>, Foo<T>)
    -> std::tuple<Ts..., Foo<T>>;

template<typename... Ts>
auto operator+(std::tuple<Ts...> t, Foo<copy_prev>)
    -> std::tuple<Ts..., select_last_t<Ts...>>;

template<typename... Ts>
using fold_t = decltype((std::tuple<>{} + ... + std::declval<Ts>()));

select_last_t实施为

template<typename T>
struct tag
{
    using type = T;
};

template<typename... Ts>
struct select_last
{
    using type = typename decltype((tag<Ts>{}, ...))::type;
};

template<typename... Ts>
using select_last_t = typename select_last<Ts...>::type;

Live

答案 1 :(得分:2)

不确定你想要什么...但是如果你的Bar结构只接受Foo类型......那就是:if可以写成如下

template <typename ...>
struct Bar;

template <typename ...Ts>
struct Bar<Foo<Ts>...>
 { /* something */ };

并在Bar中想要一个类型,以便从

开始
 Bar<Foo<int>, Foo<>, Foo<>, Foo<double>, Foo<>>

你想要内部类型

 std::tuple<Foo<int>, Foo<int>, Foo<int>, Foo<double>, Foo<double>>

Foo的未表达(默认)参数被最后表达的参数替换...我没有看到优雅的解决方案。

我能想象的最好的是如下所示的类型助手的开发(为简洁起见,我已将ctfpat重命名为CopyTypeFromPreviousArgumentTag}

template <typename...>
struct fooFolder;

// error case: the first type of Bar is ctfpat (non impemented;
// generate compile time error)
template <typename ... Ts>
struct fooFolder<std::tuple<>, ctfpat, ctfpat, Ts...>;

template <typename ... Tps, typename Tprev, typename T0, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, T0, Ts...>
   : fooFolder<std::tuple<Tps..., Foo<T0>>, T0, Ts...>
 { };

template <typename ... Tps, typename Tprev, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, ctfpat, Ts...>
   : fooFolder<std::tuple<Tps..., Foo<Tprev>>, Tprev, Ts...>
 { };

template <typename Tpl, typename Tprev>
struct fooFolder<Tpl, Tprev>
 { using type = Tpl; };

Bar成为

template <typename ...>
struct Bar;

template <typename ...Ts>
struct Bar<Foo<Ts>...>
 {
   using foldedType = typename fooFolder<std::tuple<>, ctfpat, Ts...>::type;

   foldedType expanded;
 };

以下是完整的编译示例

#include <tuple>
#include <type_traits>

struct ctfpat // copy type from previous argument tag
 { };

template <typename T = ctfpat>
struct Foo
 { };

template <typename ...>
struct fooFolder;

// error case: the first type of Bar is ctfpat (non impemented;
// generate compile time error)
template <typename ... Ts>
struct fooFolder<std::tuple<>, ctfpat, ctfpat, Ts...>;

template <typename ... Tps, typename Tprev, typename T0, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, T0, Ts...>
   : fooFolder<std::tuple<Tps..., Foo<T0>>, T0, Ts...>
 { };

template <typename ... Tps, typename Tprev, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, ctfpat, Ts...>
   : fooFolder<std::tuple<Tps..., Foo<Tprev>>, Tprev, Ts...>
 { };

template <typename Tpl, typename Tprev>
struct fooFolder<Tpl, Tprev>
 { using type = Tpl; };

template <typename ...>
struct Bar;

template <typename ... Ts>
struct Bar<Foo<Ts>...>
 {
   using foldedType = typename fooFolder<std::tuple<>, ctfpat, Ts...>::type;

   foldedType expanded;
 };

int main()
 {
   using t1 = typename Bar<Foo<>, Foo<int>, Foo<>, Foo<double>,
                           Foo<>>::foldedType;
   using t2 = std::tuple<Foo<int>, Foo<int>, Foo<int>, Foo<double>,
                         Foo<double>>;

   static_assert( std::is_same<t1, t2>{}, "!" );
 }