重新排序一个参数包,以便std :: tie可以对它进行操作,给定一个元组

时间:2015-03-28 02:38:50

标签: c++ c++11 tuples

鉴于std::tuple<Ts...> tuple和一包类型Types... types,我们希望执行std::tie(_types...) = tuple,其中_types...通过重新排序从types...获得,以便std::tie(_types...) = tuple;将有意义(std::ignore如果_types...中找不到与types...中的任何类型相匹配的话,则会Ts...插入template <typename... Ts, typename... Types> std::tuple<Ts...> tiePackToTuple (const std::tuple<Ts...>& tuple, Types&... types); 。我正在调用此功能

#include <iostream>
#include <tuple>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;

template <typename T>
T getT (T) {return std::ignore;}  // There is a problem with this case.

template <typename T, typename... Rest>  // This overload must precede the next overload for some reason (the other way around will not compile).
T& getT (T, T& returnMe, Rest&...) {return returnMe;}  // Since returnMe is of type T.

template <typename T, typename Discard, typename... Rest> 
T& getT (T t, Discard&, Rest&... rest) {return getT (t, rest...);}  // Since Discard is not the same as T.

template <typename... Ts, typename... Types>
std::tuple<Ts...> tiePackToTuple (const std::tuple<Ts...>& tuple, Types&... types) {
    return std::tie (getT(Ts{}, types...)...) = tuple;
}

int main() {
    std::tuple<int, char, double> tuple1 = std::make_tuple(5, '?', 3.14);
    int a;  char c;  double d;
    tiePackToTuple (tuple1, d,a,c);
    show(a)  show(c)  show(d)  // a = 5, c = '?', d = 3.14

    std::cout << '\n';
    int a_, b_;  char c_;  double d_;
    std::tuple<int, char, int, double> tuple2 = std::make_tuple(5, '?', 8, 3.14);
    tiePackToTuple (tuple2, d_, a_, c_, b_);
    show(a_)  show(b_)  show(c_)  show(d_)  // Incorrect (a_ = 8, b_ = gibberish).
     // Should be a_ = 5, b_ = 8.
}

这是我到目前为止所拥有的。

std::tuple<int, char, int, double> tuple2 = std::make_tuple(5, '?', 8, 3.14);

指示main()中的输出错误。问题是有两种int类型,我希望a_成为第一个int(即5),b_是第二个int(带有8)。显然编译器感到困惑,但我不知道如何解决这个问题。

另一个问题是,如果我用

替换main()中的std::tuple<int, char, int, double, bool> tuple2 = std::make_tuple(5, '?', 8, 3.14, false);
T getT (T) {return std::ignore;}

它不会编译,因为我的std::ignore显然是编译器的问题,因此我不知道如何在需要插入{{1}}时插入它。怎么做那部分?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

更新:T.C.解决了这个问题。我们现在可以使这个问题更加有趣:

鉴于std::tuple<Ts...> tuple和一包类型Types... types,我们希望执行std::tie(types...) = tuple',其中tuple'tuple获得以下意义: 假设Types...{int, char, int, int, double, int, int, bool, int}tuple = {0.1,3,4,'?'}属于std::tuple<double,int,int,char>类型。 然后tuple'将是std::make_tuple(3,'?',4,3,0.1,4,3,bool(),4)。请注意,Types...中不存在tuple中不存在的类型的默认构造函数。 这是我对T.C。解决方案的概括(归功于他的原因),但默认的构造函数案例还没有被覆盖。

更新:现在处理的默认构造函数案例。无法想到任何进一步的概括,所以我会在这里结束。

#include <iostream>
#include <tuple>
#include <type_traits>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;

template <std::size_t...> struct index_sequence {};

template <std::size_t N, std::size_t... Is>
struct make_index_sequence_helper : make_index_sequence_helper<N-1, N-1, Is...> {};

template <std::size_t... Is>
struct make_index_sequence_helper<0, Is...> {
    using type = index_sequence<Is...>;
};

template <std::size_t N>
using make_index_sequence = typename make_index_sequence_helper<N>::type; 

// Given a pack of types Z, a number N, and a type T, count the number of times T appears in the first N types of Z.
template <std::size_t, typename, typename, std::size_t Count = 0> struct CountInFirstN;

template <std::size_t N, typename T, template <typename...> class Z, std::size_t Count, typename First, typename... Rest>
struct CountInFirstN<N, T, Z<First, Rest...>, Count> : CountInFirstN<N-1, T, Z<Rest...>, Count> {};

template <std::size_t N, template <typename...> class Z, typename T, std::size_t Count, typename... Rest>
struct CountInFirstN<N, T, Z<T, Rest...>, Count> : CountInFirstN<N-1, T, Z<Rest...>, Count+1> {};

template <typename T, template <typename...> class Z, std::size_t Count, typename First, typename... Rest>
struct CountInFirstN<0, T, Z<First, Rest...>, Count> : std::integral_constant<std::size_t, Count> {};

template <typename T, template <typename...> class Z, std::size_t Count, typename... Rest>
struct CountInFirstN<0, T, Z<T, Rest...>, Count> : std::integral_constant<std::size_t, Count> {};  // Without this, there will be ambiguity compiling error with CountInFirstN<N, T, Z<T, Rest...>, Count>.

// Given a pack of types Z, a 0-based number N, and a type T, determine the index of the Nth T in Z.
template <std::size_t, typename, typename, std::size_t = 0> struct GetIndexInPack;

template <std::size_t N, typename T, template <typename...> class Z, std::size_t V, typename First, typename... Rest>
struct GetIndexInPack<N, T, Z<First, Rest...>, V> : GetIndexInPack<N, T, Z<Rest...>, V+1> {};

template <std::size_t N, template <typename...> class Z, typename T, std::size_t V, typename... Rest>
struct GetIndexInPack<N, T, Z<T, Rest...>, V> : GetIndexInPack<N-1, T, Z<Rest...>, V+1> {};

template <typename T, template <typename...> class Z, std::size_t V, typename... Rest>
struct GetIndexInPack<0, T, Z<T, Rest...>, V> : std::integral_constant<std::size_t, V> {};   // The T's must match so that when N is 1, we get the value immediately through inheritence from GetIndexInPack<0, T, Z<T, Rest...>, V>.

constexpr std::size_t NOT_FOUND = -1;

template <std::size_t N, typename T, template <typename...> class Z, std::size_t V>
struct GetIndexInPack<N, T, Z<>, V> : std::integral_constant<std::size_t, NOT_FOUND> {};

// NumTypes<T, Z<Types...>::value is the number of times T occurs in Types...
template <typename, typename, std::size_t = 0> struct NumTypes;

template <typename T, template <typename...> class Z, typename First, typename... Rest, std::size_t Count>
struct NumTypes<T, Z<First, Rest...>, Count> : NumTypes<T, Z<Rest...>, Count> {};

template <typename T, template <typename...> class Z, typename... Rest, std::size_t Count>
struct NumTypes<T, Z<T, Rest...>, Count> : NumTypes<T, Z<Rest...>, Count+1> {};

template <typename T, template <typename...> class Z, std::size_t Count>
struct NumTypes<T, Z<>, Count> : std::integral_constant<std::size_t, Count> {};

// Given a pack of types Z, produce a list of numbers Ns, where Ns[i] is the number of times Z[i] has appeared in the first i items in Z.
template <typename, typename, typename> struct GenerateCounts;

template <template <typename...> class Z, typename... Types, typename... Ts, std::size_t... Is>
struct GenerateCounts<Z<Types...>, Z<Ts...>, index_sequence<Is...>> :
    index_sequence<CountInFirstN<Is % (NumTypes<Types, Z<Ts...>>::value + 1), Types, Z<Types...>>::value...> {};

// GetTupleElement is needed to take care of the default constructor case, in the event that GetIndexInPack<Count, Type, std::tuple<Ts...>>::value == NOT_FOUND.
template <std::size_t N, typename Type, typename... Ts>
struct GetTupleElementHelper {
    static Type get (const std::tuple<Ts...>& tuple) {return std::get<N>(tuple);}
};

template <typename Type, typename... Ts>
struct GetTupleElementHelper<NOT_FOUND, Type, Ts...> {
    static Type get (const std::tuple<Ts...>& tuple) {return Type();}
};

template <std::size_t Count, typename Type, typename... Ts>
struct GetTupleElement {
    static Type get (const std::tuple<Ts...>& tuple) {
        constexpr std::size_t index = GetIndexInPack<Count, Type, std::tuple<Ts...>>::value;
        return GetTupleElementHelper<index, Type, Ts...>::get(tuple);  // 'return index == NOT_FOUND ? Type() : std::get<index>(tuple);' will not compile because both are evaluated.
    }
};

// Now tieAnyPackToTuple itself.
template <typename... Ts, typename... Types, std::size_t... Counts>
void tieAnyPackToTupleHelper (const std::tuple<Ts...>& tuple, index_sequence<Counts...>, Types&... types) {
    std::tie(types...) = std::make_tuple (GetTupleElement<Counts, Types, Ts...>::get(tuple)...);
}

template <typename... Ts, typename... Types>
void tieAnyPackToTuple (const std::tuple<Ts...>& tuple, Types&... types) {
    tieAnyPackToTupleHelper (tuple, GenerateCounts<std::tuple<Types...>, std::tuple<Ts...>, make_index_sequence<sizeof...(Types)>>{}, types...);
}

int main() {
    std::cout << NumTypes<int, std::tuple<int, char, int, int, double, int, int, bool, int>>::value << std::endl;  // 6

    std::tuple<int, char, double> tuple1 = std::make_tuple(5, '?', 3.14);
    int a;  char c;  double d;
    tieAnyPackToTuple (tuple1, d,a,c);
    show(a)  show(c)  show(d)  // a = 5, c = '?', d = 3.14
    std::cout << '\n';

    int m, n, r, s, t, u;  char p;  double q;
    std::tuple<int, char, int, double> tuple2 = std::make_tuple(5, '?', 8, 3.14);
    tieAnyPackToTuple (tuple2, q, m, n, p);
    show(m)  show(n)  show(p)  show(q)  // m = 5, n = 8, p = '?', q = 3.14
    std::cout << '\n';

    tieAnyPackToTuple (tuple2, m, p, n, r, q, s, t, u);
    show(m)  show(n)  show(p)  show(q)  show(r)  show(s)  show(t)  show(u)  // m = 5, n = 8, p = '?', q = 3.14, r = 5, s = 8, t = 5, u = 8 

    bool z;  long l;
    tieAnyPackToTuple (tuple2, m, p, l, n, r, q, s, t, z, u);  // l is long and z is bool but there is no long or bool element in tuple2.
    show(l)  std::cout << std::boolalpha << "z = " << z << std::endl;  // l = 0, z = false  (the default values of long and bool).
}