c++ - 递归可迭代模板函数 C++

标签 c++ arrays recursion

我对如何将类型信息传递到循环链感到困惑。

以下代码的目标是做一些类似于 python 的 ",".join(["a","b","c"]) 的事情,但是是递归的,这样我就可以加入由...组成的字符串数组的数组,其中的分隔符与结构的深度一样多。

这是我稍微注释掉的(试图让它编译)代码:

#include <iostream>
#include <typeinfo>
#include <vector>


template <typename RAI>
std::string string_join(RAI begin, RAI end, std::string delimiter) {
    if (begin == end) { return ""; }
    std::string joint = std::to_string(*begin);
    begin++;
    for (; begin != end; begin++) {
        joint += delimiter + std::to_string(*begin);
    }
    return joint;
}


template <typename RAI, int depth>
struct string_join_recursive {
    std::string operator()(RAI iterator, std::string *delimiter, int *dimensions) {
        typedef typename std::iterator_traits<RAI>::value_type::iterator value_iterator_type;
        std::cout << typeid(value_iterator_type).name() << " " << typeid(std::begin(*iterator)).name() << std::endl;
        std::string joint = string_join_recursive<value_iterator_type, depth-1>(std::begin(*iterator), delimiter+1, dimensions+1);
        iterator++;
        for (int i=1; i<*dimensions; i++) {
            //joint += *delimiter + string_join_recursive<value_iterator_type, depth-1>(std::begin(*iterator), delimiter+1, dimensions+1);
            iterator++;
        }
        return "";//joint;
    }
};

template <typename RAI>
struct string_join_recursive<RAI,1> {
    std::string operator()(RAI iterator, std::string *delimiter, int *dimensions) {
        return string_join(iterator, iterator + *dimensions, *delimiter);
    }
};

int main() {
    std::vector<std::vector<int>> a;
    std::vector<int> b = {1,2,3};
    std::vector<int> c = {1,2,4};
    std::string delimiters[2] = {"\n",","};
    int dimensions[2] = {2, 3};
    std::cout << string_join_recursive<std::vector<std::vector<int>>::iterator, 2>()(a.begin(), delimiters, dimensions) << std::endl;
}

我不是资深的 c++ 程序员,所以这可能有不止一个问题,但目前,它甚至无法编译。显然 value_iterator_type 不是我认为的程序运行时的类型,但我不确定它到底是什么。 OTOH,当我注释掉第 5 行时,它会编译,并且上面的打印行表明这两个东西是同一类型。

这是我遇到的错误:

error: no matching function for call to ‘string_join_recursive<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, 1>::string_join_recursive(std::vector<int>::iterator, std::__cxx11::string*, int*)’
         std::string joint = string_join_recursive<value_iterator_type, depth-1>(std::begin(*iterator), delimiter+1, dimensions+1);

以更清洁的方式做这件事的方法加分。 使这项工作与任意可迭代对象(数组等)一起工作的方法的更多奖励积分

最佳答案

这是我将如何处理它的草图:

#include <iostream>
#include <algorithm>
#include <type_traits>
#include <sstream>
#include <vector>
#include <tuple>

// this allows us to test if a element should be recursed into

template <typename iterable, typename tag = void>
struct is_iterable
{
    static constexpr bool value = false;
};

template <typename iterable>
struct is_iterable <iterable, std::void_t <decltype (std::begin (std::declval <iterable> ()))>>
{
    static constexpr bool value = true;
};

template <typename iterable>
constexpr bool is_iterable_v = is_iterable <iterable>::value;

// visit elements of a tuple

template <size_t Index, typename visitor, typename ...types>
void visit_tuple (std::tuple <types...> const & Tuple, visitor && Visitor)
{
    Visitor (std::get <Index> (Tuple));

    constexpr auto nextIndex = Index + 1;

    if constexpr (nextIndex < sizeof... (types))
        visit_tuple <nextIndex> (Tuple, Visitor);
}

// this function generically walks the elements of a container, with an overload for tuples and pairs

template <typename container, typename visitor>
std::enable_if_t <is_iterable_v <container>>
visit_elements (container const & Container, visitor && Visitor)
{
    for (auto && Element: Container)
        Visitor (Element);
}

template <typename visitor, typename ...element_types>
void visit_elements (std::tuple <element_types...> const & Tuple, visitor && Visitor)
{
    if constexpr (sizeof... (element_types) > 0)
        visit_tuple <0> (Tuple, Visitor);
}

template <typename visitor, typename first_type, typename second_type>
void visit_elements (std::pair  <first_type, second_type> const & Pair, visitor && Visitor)
{
    Visitor (Pair.first);
    Visitor (Pair.second);
}

// type trait for testing if a value is "visitable"

struct dummy_visitor { template <typename type> void operator () (type &&); };

template <typename container, typename tag = void>
struct is_visitable
{
    static constexpr bool value = false;
};

template <typename container>
struct is_visitable <container, std::void_t <decltype (visit_elements (std::declval <container> (), dummy_visitor()))>>
{
    static constexpr bool value = true;
};

template <typename container>
constexpr bool  is_visitable_v =  is_visitable <container>::value;

// this function walks each item and either emits it or recurses into it

template <typename iterable>
bool join_impl (std::ostream & os, char delim, bool emitted_anything, iterable const & Iterable)
{
  using std::begin;
  using std::end;

  visit_elements (Iterable, [&] (auto && Element) {

    if constexpr (!is_visitable_v <decltype (Element)>)
    {
        if (emitted_anything)
          os << delim;

        os << Element;

        emitted_anything = true;
    }
    else
    {
      emitted_anything = join_impl (os, delim, emitted_anything, Element);
    }

  });

  return emitted_anything;
}

// these are wrappers to adapt join_impl for different use cases

template <typename container>
struct joiner
{
    char delim;
    container const & Container;

    operator std::string () const { return to_string <char> (); }

    template <typename char_t>
    std::basic_string <char_t> to_string () const;
};

template <typename container>
std::ostream & operator << (std::ostream & os, joiner <container> j)
{
    bool emitted_anything = false;
    join_impl (os, j.delim, emitted_anything, j.Container);
    return os;
}

template <typename container>
template <typename char_t>
std::basic_string <char_t> joiner <container>::to_string () const
{
    std::ostringstream os;
    os << *this;
    return os;
}

template <typename container>
std::enable_if_t <is_visitable_v <container>, joiner <container>>
join (char delim, container const & Container)
{
    return joiner <container> { delim, Container };
}

// test the streaming use case

int main ()
{
    std::vector <std::vector <std::tuple <int, float>>> x {
        { { 1, 1.0f }, { 2, 2.0f }},
        { { 3, 3.0f }, { 4, 4.0f }},
    };

    std::cout << join (',', x) << std::endl;
}

这应该只适用于支持标准迭代协议(protocol)的事物的任何递归。它还支持元组和对。通过定义访问每个公开字段的 visit_elements 重载,可以将 In 扩展为用户定义结构。 is_iterable 类型特征需要在其预期接口(interface)中更加精确。

关于c++ - 递归可迭代模板函数 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46941912/

相关文章:

c++ - 递归函数c++的段错误

c++ - 如何使用 SendBuffer 在 UDP 上将结构作为数据发送?

c++ - native /CLI C++ : How can I convert from vector<class1_native> to List<class1_cli>

c++ - 在 Windows 中编译 Clang 插件

java - 访问 ArrayList 中的元素

algorithm - 如何在不递归的情况下通过 +3 或 *5 操作获取目标数字?

c++ - 理解 ask --true 0 不是数字 0

java - 如何在 Kotlin 中将字符串拆分为数组?

c - 运行 scanf() 时出错,tute 使用 gets()

c - 通过引用传递给递归函数