接受元组并返回另一个元组的函数

时间:2017-10-02 13:01:05

标签: c++ function tuples c++14 overloading

如何编写一个函数重载,它接受任意大小的元组并返回另一个相同大小的元组,其中int变为double(添加0.5值),char变为string,size_t变为int(变为相反的符号) ),以及我们可能想要的任何其他类型的改变。例如,函数foo获取tuple<int, char, size_t>并返回tuple<double, string, int>,而tuple<size_t, char>则返回tuple<int, string>

3 个答案:

答案 0 :(得分:7)

这是一种方式:

步骤1 - 声明转换器的概念:

template<class From>
struct converter;

第2步 - 定义它的一些特化。这些拼写了转换规则。

template<> struct converter<int>
{
    template<class Arg>
    auto operator()(Arg&& arg) const {
        return std::size_t(arg);
    }
};

template<> struct converter<char>
{
    template<class Arg>
    auto operator()(Arg&& arg) const {
        return std::string(1, arg);
    }
};

template<> struct converter<std::size_t>
{
    template<class Arg>
    auto operator()(Arg&& arg) const {
        using int_type = long long;
        auto result = int_type(arg);
        return -result;
    }
};

步骤3 - 根据输入元组,输入元组中每个索引处的类型(输入元组和输入元组的转换)编写转换函数(这个有点讨厌):

template<class Tuple, std::size_t...Is>
auto convert_impl(Tuple&& t, std::index_sequence<Is...>)
{
    using tuple_type = std::decay_t<Tuple>;

    return std::make_tuple(converter<std::tuple_element_t<Is, tuple_type>>()(std::get<Is>(std::forward<Tuple>(t)))...);
}

第4步 - 提供易于使用的界面:

template<class Tuple>
auto convert(Tuple&& t)
{
    using tuple_type = std::decay_t<Tuple>;
    return convert_impl(std::forward<Tuple>(t), 
                        std::make_index_sequence<std::tuple_size<tuple_type>::value>());
}

第5步 - 写一个测试:

int main()
{
    auto t = convert(std::make_tuple(int(1), char('a'), std::size_t(6)));   
}

此解决方案还具有完美转发作为奖励。

答案 1 :(得分:1)

很抱歉,但以下解决方案不是c ++ 11,而是c ++ 14(可以针对c ++ 11进行修改,但并不是一件容易的事)

首先,你需要一个模板函数来(不)转换泛型值(你不想修改的那些)

template <typename T>
T conv (T const & t)
 { return t; }

接下来,您需要进行转换

double conv (int const & i)
 { return i + 0.5; }

std::string conv (char const & c)
 { return { c }; }

int conv (std::size_t const & s)
 { return -s ; }

此时(使用辅助函数接收元组类型的索引列表),使用c ++ 14返回auto类型工具,它非常简单

template <typename ... Ts, std::size_t ... Is>
auto convTH (std::tuple<Ts...> const & t,
             std::index_sequence<Is...> const &)
 { return std::make_tuple( conv(std::get<Is>(t))... ); }

template <typename ... Ts>
auto convT (std::tuple<Ts...> const & t)
 { return convTH(t, std::make_index_sequence<sizeof...(Ts)>{}); }

以下是一个完整的工作示例

#include <tuple>
#include <utility>
#include <iostream>

template <typename T>
T conv (T const & t)
 { return t; }

double conv (int const & i)
 { return i + 0.5; }

std::string conv (char const & c)
 { return { c }; }

int conv (std::size_t const & s)
 { return -s ; }

template <typename ... Ts, std::size_t ... Is>
auto convTH (std::tuple<Ts...> const & t,
             std::index_sequence<Is...> const &)
 { return std::make_tuple( conv(std::get<Is>(t))... ); }

template <typename ... Ts>
auto convT (std::tuple<Ts...> const & t)
 { return convTH(t, std::make_index_sequence<sizeof...(Ts)>{}); }


int main ()
 {
   std::tuple<long, int, char, std::size_t, unsigned long long>
      t0 = { 0L, 1, '2', 3U, 4ULL };

   auto t1 = convT(t0);

   static_assert( std::is_same<decltype(t1),
      std::tuple<long, double, std::string, int, unsigned long long>>{}, "!");

   std::cout << "0) " << std::get<0U>(t1) << std::endl;
   std::cout << "1) " << std::get<1U>(t1) << std::endl;
   std::cout << "2) " << std::get<2U>(t1) << std::endl;
   std::cout << "3) " << std::get<3U>(t1) << std::endl;
   std::cout << "4) " << std::get<4U>(t1) << std::endl;
 }

答案 2 :(得分:0)

这里是返回类型的C ++ 11解决方案,因此无需使用auto

#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
#include <tuple>

template <typename T, typename Map> struct FindCounterpartType;

template <typename T, template <typename...> class M, template <typename, typename> class P, typename U, typename... Pairs>
struct FindCounterpartType<T, M<P<T,U>, Pairs...>> {
    using type = U;
};

template <typename T, template <typename...> class M, typename Pair, typename... Pairs>
struct FindCounterpartType<T, M<Pair, Pairs...>> : FindCounterpartType<T, M<Pairs...>> {};

template <typename Pack, typename Map, typename Output> struct ChangeTypesHelper;

template <template <typename...> class P, typename... Output, typename Map>
struct ChangeTypesHelper<P<>, std::tuple<Output...>, Map> {
    using type = P<Output...>;
};

template <template <typename...> class P, typename First, typename... Rest, typename... Output, typename Map>
struct ChangeTypesHelper<P<First, Rest...>, std::tuple<Output...>, Map> : ChangeTypesHelper<P<Rest...>, std::tuple<Output..., typename FindCounterpartType<First, Map>::type>, Map> {};

template <typename Pack, typename Map>
struct ChangeTypes : ChangeTypesHelper<Pack, std::tuple<>, Map> {};

// Testing
template <typename...> struct M;
template <typename...> struct Pack;
template <typename, typename> struct P;

using Map = M< P<int, std::string>, P<char, std::size_t>, P<double, bool> >;

int main() {
    std::cout << std::boolalpha << std::is_same<
        ChangeTypes<std::tuple<int, char, double>, Map>::type, 
        std::tuple<std::string, std::size_t, bool>
    >::value << '\n';

    std::cout << std::is_same<
        ChangeTypes<Pack<char, double, double, int, int, int, char>, Map>::type, 
        Pack<std::size_t, bool, bool, std::string, std::string, std::string, std::size_t>
    >::value << '\n';

    std::cin.get();
}