【问题标题】:Function that accepts a tuple and returns another tuple接受一个元组并返回另一个元组的函数
【发布时间】:2017-10-02 13:01:05
【问题描述】:

如何编写一个函数重载,它接受任意大小的元组并返回另一个相同大小的元组,其中 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>

【问题讨论】:

  • 并且 size_t 变为 int(通过变为相反的符号)size_t 不需要是 int。在许多(可能是全部)64 位系统上,它将是longlong long。那么你想要size_t的签名版本吗
  • 为什么你想这样做?它应该解决什么问题?你的用例是什么?也许您应该阅读一些关于 the XY problem 的内容并重新考虑您的问题。
  • 一个有趣的练习(可能是我没有投反对票的原因)。但是看到任何“我如何从头开始做这个”问题都太宽泛了,这个也是。
  • @NathanOlver 这只是一个例子,我尽量保持简单以了解主要思想。
  • 主要想法是您希望我们免费为您编写一段不平凡的代码,并带有扩展点?

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


【解决方案1】:

这是一种方法:

第 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 步 - 根据输入元组、indecies 序列和输入元组中每个索引处的类型的转换器编写一个转换函数(这个有点讨厌):

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

此解决方案还具有完美转发功能。

【讨论】:

  • 它可能有好处,但它不符合 OP 标记的 C++11 要求。尽管如此,还是很好地展示了 C++14,我希望 OP 只是弄错了。
  • 将我的标签更改为 C++14。
  • 好了,现在靶心已经围绕箭头绘制了。 +1
  • @StoryTeller 哈哈谢谢。真的有人再使用 c++11 了吗?
  • @RichardHodges - 很多人。一些商店刚刚完成了采用。
【解决方案2】:

抱歉,下面的解决方案不是 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;
 }

【讨论】:

    【解决方案3】:

    这里有一个返回类型的 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();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-03
      • 2011-07-17
      相关资源
      最近更新 更多