基于位置的包装中的特定类型

时间:2017-10-21 19:07:55

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

take_from_args<foo<int, bool, char, float>, 0,2>::type将是

foo<int, char>基于位置0和2.实施很简单:

template <typename Class, std::size_t... Positions>
struct take_from_args;

template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct take_from_args<P<Ts...>, Is...> {
    using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>; 
};

现在,让我们尝试将此应用于此类:

template <int V, bool B, typename... Args>
struct bar {};

问题是bar的int和bool参数,因此无法传递到take_from_args。所以我们来定义:

template <int V, bool B>
struct bar_h {
    template <typename... Args>
    using templ = bar<V, B, Args...>;   
};

不幸的是,take_from_args<bar_h<5, true>::templ<int, bool, char, float>, 0,2>::type无法编译。如何重新定义take_from_args以便它可以模拟像bar这样的类?

我的整个代码:

#include <tuple>

template <typename Class, std::size_t... Positions> struct take_from_args;

template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct take_from_args<P<Ts...>, Is...> {
    using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>; 
};


// Testing
template <typename... Args>
struct foo {};

template <int V, bool B, typename... Args>
struct bar {};

template <int V, bool B>
struct bar_h {
    template <typename... Args>
    using templ = bar<V, B, Args...>;   
};

int main() {
    static_assert(std::is_same<
        take_from_args<foo<int, bool, char, float>, 0,2>::type,
        foo<int, char>>::value);
//  static_assert(std::is_same<
//      take_from_args<bar_h<5, true>::templ<int, bool, char, float>, 0,2>::type,
//      bar<5, true, int, char>>::value);
}

2 个答案:

答案 0 :(得分:2)

bar_h的解决方法不起作用,因为bar_h<5, true>::templ<int, bool, char, float>只是bar<5, true, int, bool, char, float>别名

static_assert(// compiles without error
  std::is_same_v<
    bar_h<5, true>::templ<int, bool, char, float>,
    bar<5, true, int, bool, char, float>
  >
);

我看到两个选项:

1。避免在`bar`

中使用非类型模板参数

修改:正如您在your answer中遵循此方法,但我遇到了issues with clang:这是一个修改后的版本works for me与GCC 7.2和Clang 5.0

template<auto...> struct Vals {};

template<class T>
struct HasVals : std::false_type {};

template<auto... Vs>
struct HasVals<Vals<Vs...>> : std::true_type {};

template<class T, size_t... is>
struct take_from_args;

template<template<class...> class P, class... Ts, size_t... is>
struct take_from_args<P<Ts...>, is...> {
// convention: pass-through first argument if it `HasVals`
  using Vs = std::tuple_element_t<0, std::tuple<Ts...>>;
  using type = std::conditional_t<
    HasVals<Vs>::value,
    P<Vs, std::tuple_element_t<1u+is, std::tuple<Ts...>>...>,
    P<std::tuple_element_t<is, std::tuple<Ts...>>...>
  >;
};

// Testing
template<class Vs, class... Args>
struct bar;

template<int v, bool b, class... Args>
struct bar<Vals<v, b>, Args...> {
    static constexpr int value = v;
    static constexpr bool truth = b;
};

2.为`take_from_args`

提供更多专业化

由于你特别要求后者,这里有一个例子:

// there could be 1 value(s) at the beginning...
template<
  template<auto, auto, typename...> class P,
  auto v0, class... Ts, std::size_t... is
> struct take_from_args<P<v0, Ts...>, is...> {
  using type = P<v0, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};

// ... 2 ...
template<
  template<auto, auto, typename...> class P,
  auto v0, auto v1, class... Ts, std::size_t... is
> struct take_from_args<P<v0, v1, Ts...>, is...> {
  using type = P<v0, v1, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};

// ... 3 ... and more?
template<
  template<auto, auto, typename...> class P,
  auto v0, auto v1, auto v2, class... Ts, std::size_t... is
> struct take_from_args<P<v0, v1, v2, Ts...>, is...> {
  using type = P<v0, v1, v2, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};

不幸的是,我没有成功使用auto...来扣除主要模板参数。

答案 1 :(得分:0)

遵循朱利叶斯关于重新设计班级bar的最后建议:

#include <tuple>
#include <type_traits>

template <auto...> struct Vals {};

template <typename Class> struct HasVals : std::false_type {};

template <auto... Vs>
struct HasVals<Vals<Vs...>> : std::true_type {};

template <typename Class, std::size_t... Positions> struct take_from_args;

template <template <typename, typename...> class P, typename VALS, typename... Ts, std::size_t... Is>
struct take_from_args<P<VALS, Ts...>, Is...> {
    using type = std::conditional_t<HasVals<VALS>::value,
        P<VALS, std::tuple_element_t<Is, std::tuple<Ts...>>...>,
        P<std::tuple_element_t<Is, std::tuple<VALS, Ts...>>...>  // VALS is part of the tuple in this case
    >;
};

// Testing
template <typename... Args>
struct foo {};

template <typename VALS, typename... Args>
struct bar;

template <int v, bool b, typename... Args>
struct bar<Vals<v, b>, Args...> {
    static constexpr int value = v;
    static constexpr bool truth = b;
};

int main() {
    static_assert(std::is_same<
        take_from_args<foo<int, bool, char, float>, 0,2>::type,
        foo<int, char>>::value);

    using new_bar = take_from_args<bar<Vals<5, true>, int, bool, char, float>, 0,2>::type;

    static_assert(std::is_same<
        new_bar,
        bar<Vals<5, true>, int, char>>::value);

    static_assert(new_bar::value == 5);
    static_assert(new_bar::truth == true);
    //static_assert(std::is_same<decltype(new_bar::value), int>::value);  // why fails?
    //static_assert(std::is_same<decltype(new_bar::truth), bool>::value);

}