【问题标题】:How to write function for generic nested container in c++17?如何在 c++17 中为通用嵌套容器编写函数?
【发布时间】:2021-04-23 18:50:07
【问题描述】:

我想用自定义分配器实现一个参数类型为List<List<int>> 的函数,其中List 可能是std::vectorstd::list,甚至std::vector,同时它还接受包含的原始大括号表达

目前我有一个本地尝试如下

#include <iostream>
#include <list>
#include <vector>

template<typename LLO = std::vector<std::vector<int>>>
void f(const LLO& a) {
   for (const auto& i : a) {
      for (const auto& j : i) {
         std::cout << j;
      }
      std::cout << '\n';
   }
}

template<typename LO = std::vector<int>, int = 0>
void f(const std::initializer_list<LO>& a) {
   f(std::vector(a));
}

int main() {
   f({{1, 2}, {3, 4}});
   f({std::vector{1, 2}, {3, 4}});
   f(std::list{std::list{1, 2}, {3, 4}});
   f({std::list{1, 2}, {3, 4}});
}

最重要的测试是f({std::list{1, 2}, {3, 4}}),它要求我显式地为​​ std::initializer_list 写一个丑陋的重载。

实际上,在我的情况下,我有三个这样的嵌套容器类型的参数,如果我使用这个解决方案,我必须编写2^3=8重载函数

我想知道是否有一些好看的解决方案,我的代码是c++17下的,如果可以在c++20中用概念轻松实现,这次对我没有帮助,但我是还是很好奇。

【问题讨论】:

标签: c++ templates stl containers c++17


【解决方案1】:

如果您可以容忍使用辅助功能,您可以这样做:

#include <iostream>
#include <list>
#include <vector>

template<class LLO>
LLO
make_param(LLO&& a) { return std::forward<LLO>(a); }

template<template<class...> class Cont = std::vector, class T = int, class... Rest>
auto
make_param(std::initializer_list<Cont<T, Rest...>> ilist)
{
    return Cont<Cont<T, Rest...>, Rest...>(ilist);
}


template<class LLO>
void f(const LLO& p)
{
    //code here
}

template<class LLO1, class LLO2>
void f(const LLO1& p1, const LLO2& p2)
{
    //more code here
}

int main()
{
    f(make_param({{1, 2}, {3, 4}}));
    f(make_param({std::vector{1, 2}, {3, 4}}));
    f(make_param(std::list{std::list{1, 2}, {3, 4}}));
    f(make_param({std::list{1, 2}, {3, 4}}));

    std::vector<int, std::allocator<float>> v;
    f(make_param({v, v}));

    f(make_param({{1, 2}, {3, 4}}), make_param({std::list{1, 2}, {3, 4}}));
}

另一个不必为每个参数都使用辅助函数的解决方案可能是这样的:

#include <iostream>
#include <list>
#include <vector>
#include <tuple>

template<class Item, template<class...> class Tuple, class... Types, std::size_t... Is>
std::tuple<Types..., Item>
tuple_append_impl(Item&& item, Tuple<Types...>& t, std::index_sequence<Is...>)
{
    return { std::move(std::get<Is>(t))..., std::forward<Item>(item) };
}

template<class Item, class... Types>
std::tuple<Types..., Item>
tuple_append(Item&& item, std::tuple<Types...>& t)
{
    return tuple_append_impl(std::forward<Item>(item), t, std::make_index_sequence<sizeof...(Types)>());
}

template<class... Conts>
struct param
{
    template<class LLO>
    param<Conts..., LLO>
    operator()(LLO&& a)
    {
        return { tuple_append(std::forward<LLO>(a), conts) };
    }

    template<template<class...> class Cont = std::vector, class T = int, class... Rest>
    param<Conts..., Cont<Cont<T, Rest...>, Rest...>>
    operator()(std::initializer_list<Cont<T, Rest...>> ilist)
    {
        return { tuple_append(Cont<Cont<T, Rest...>, Rest...>(ilist), conts) };
    }

    std::tuple<Conts...> conts;
};

template<>
struct param<>
{
    template<class LLO>
    param<LLO>
    operator()(LLO&& a) { return {std::forward<LLO>(a)}; }

    template<template<class...> class Cont = std::vector, class T = int, class... Rest>
    param<Cont<Cont<T, Rest...>, Rest...>>
    operator()(std::initializer_list<Cont<T, Rest...>> ilist)
    {
        return {Cont<Cont<T, Rest...>, Rest...>(ilist)};
    }
};

param() -> param<>;

template<class Cont>
void f(const param<Cont>& p)
{
    //function taking one param
    auto& llo = std::get<0>(p.conts);
}

template<class Cont1, class Cont2>
void f(const param<Cont1, Cont2>& p)
{
    //function taking two param
    auto&& llo0 = std::get<0>(p.conts);
    auto&& llo1 = std::get<1>(p.conts);
}

template<class Cont1, class Cont2, class Cont3>
void f(const param<Cont1, Cont2, Cont3>& p)
{
    //function taking three param
}

template<class... Conts>
void f2(const param<Conts...>& p)
{
    //function taking arbitrary amount of params
}

int main()
{
    f(param<>()({{1, 2}, {3, 4}}));
    f(param<>()({std::vector{1, 2}, {3, 4}}));
    f(param<>()(std::list{std::list{1, 2}, {3, 4}}));
    f(param<>()({std::list{1, 2}, {3, 4}}));

    f(param<>()({{1, 2}, {3, 4}})
            ({std::list{1, 2}, {3, 4}})
            (std::list{std::list{1, 2}, {3, 4}}));
}

【讨论】:

  • 你会如何在这些fs 中使用p
  • 在第一个例子中,就像 OP。我已经编辑了第二个示例来展示如何访问容器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多