【问题标题】:How to print a n-dimensional c++ STL container? Container is array of arrays or vectors of vectors如何打印 n 维 c++ STL 容器?容器是数组的数组或向量的向量
【发布时间】:2021-12-06 20:15:57
【问题描述】:

我有一个代码,我想在其中显示表示为向量的向量或 std::arrays 的 std::arrays 的张量。目的是以 numpy 打印它们的方式打印它们。我仍在学习 C++ 元编程,并想探索如何使用函数模板打印 n-dim 容器,该函数模板可以获取这个容器容器并递归地遍历它并返回一个我以后可以计算出来的字符串。

Numpy 示例:

>>> np.ones([2,2])
array([[1., 1.],
       [1., 1.]])
>>> np.ones([2,2,4])
array([[[1., 1., 1., 1.],
        [1., 1., 1., 1.]],

       [[1., 1., 1., 1.],
        [1., 1., 1., 1.]]])
>>> np.ones(4)
array([1., 1., 1., 1.])
>>> 

目前尝试过: 我尝试将此处接受的响应作为响应:

Print simply STL vectors of vectors recursively in C++

它对我来说确实适用于 2 个暗向量,但是当我将 printContainer() 的调用更改为 printContainerV2() 内的 printContainerV2() 时,我的 3d 向量编译失败了:

代码

#include <iostream>
#include <iterator>
#include <vector>

template <typename Iter, typename Cont>
bool isLast(Iter iter, const Cont& cont)
{
    return (iter != cont.end()) && (next(iter) == cont.end());
}


template <typename T>
struct is_cont {
    static const bool value = false;
};

template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
    static const bool value = true;
};


template <typename T>
std::string printContainer(T const& container)
{
    std::string str = "{";
    for (auto it = std::begin(container); it != std::end(container); ++ it)
        if (isLast(it, container))
                str = str + std::to_string(*it) + "}";
        else
                str = str + std::to_string(*it) + ",";
    return str;
}

template<typename T>
using if_not_cont = std::enable_if<!is_cont<T>::value, T>;

template<typename T>
using if_cont = std::enable_if<is_cont<T>::value, T>;

template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
    std::string str = "{";
    for (auto it = std::begin(container); it != std::end(container); ++ it)
        if (isLast(it, container))
                str = str + std::to_string(*it) + "}";
        else
                str = str + std::to_string(*it) + ",";
    return str;
}

template <typename T, typename std::enable_if<is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
    std::string str = "{";
    for (auto it = std::begin(container); it != std::end(container); ++ it)
        if (isLast(it, container))
                str = str + printContainerV2(*it) + "}";
        else
                str = str + printContainerV2(*it) + ",";
    return str;
}

int main()
{
    std::vector<int> A({2,3,6,8});
    std::vector<std::vector<int>> M(2,A);
    std::vector<std::vector<std::vector<float>>> m3{{{1,2}, {3,4}},{{5,6}, {7,8}},{{1,2}, {5,9}}};

    std::cout << is_cont<decltype(A)>::value << std::endl;  // returns true !

    // for (auto it = std::begin(M); it != std::end(M); ++ it)
    // {
    //     std::cout << printContainer(*it) << std::endl; // works well std::vector<int>
    //     std::cout << is_cont<decltype(*it)>::value << std::endl; // return false :(
    // }

    // Want to use this for printing a std::vector<std::vector<int>>
    std::cout << printContainerV2(M) << std::endl; // not working !
    std::cout << printContainerV2(m3) << std::endl; // not working
}

命令clang++ --std=c++17 test.cpp test.cpp 是上面代码的名字。

我收到了这个错误

test.cpp:45:20: error: no matching function for call to 'begin'
    for (auto it = std::begin(container); it != std::end(container); ++ it)
                   ^~~~~~~~~~
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<int, nullptr>'
      requested here
                str = str + printContainerV2(*it) + "}";
                            ^
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<std::__1::vector<int,
      std::__1::allocator<int> >, nullptr>' requested here
test.cpp:80:19: note: in instantiation of function template specialization
      'printContainerV2<std::__1::vector<std::__1::vector<int, std::__1::allocator<int> >,
      std::__1::allocator<std::__1::vector<int, std::__1::allocator<int> > > >, nullptr>' requested here
     std::cout << printContainerV2(M) << std::endl; // not working !
                  ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/initializer_list:99:1: note: 
      candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against 'int'
begin(initializer_list<_Ep> __il) _NOEXCEPT
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1753:1: note: 
      candidate template ignored: could not match '_Tp [_Np]' against 'const int'
begin(_Tp (&__array)[_Np])
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1771:1: note: 
      candidate template ignored: substitution failure [with _Cp = const int]: member reference base type
      'const int' is not a structure or union
begin(_Cp& __c) -> decltype(__c.begin())
^                              ~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1779:1: note: 
      candidate template ignored: substitution failure [with _Cp = int]: member reference base type 'const int' is
      not a structure or union
begin(const _Cp& __c) -> decltype(__c.begin())
^                                    ~
1 error generated.

【问题讨论】:

  • 如果您查看您的第一个错误 "error: no matching function for call to ‘begin(const int&amp;)’",您的递归结束条件已失败,并且您已经递归了一层太深,并且正在尝试获取迭代器以获取 int 引用。 std::begin(int) 没有意义,因此出现错误。回顾您的递归并包含一个停止递归的测试并返回您尝试为其创建迭代器失败的地方。
  • 请注意,空容器缺少"}"(应该在循环之外的打印)。

标签: c++ c++17 c++14


【解决方案1】:

这是打印任意维度向量的简单方法:

template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T> &vec){
    std::cout << "[ ";
    for(const auto& t: vec){
        std::cout << t << " ";
    }
    std::cout << "] ";
    return out;
}

对于任何其他容器,您可以执行完全相同的操作。

用途:

int main()
{
    std::vector<int> v{1,2,3};
    std::cout << v << std::endl;
    std::vector<std::vector<std::vector<int>>> v_ds
        {
            {{{1,2,3},{4,5,6}},{{7,8},{9,10}}, {{1,2,3},{4,5,6}},{{7,8},{9,10}}}
        };
    std::cout << v_ds << std::endl;
    return 0;
}

编辑: 这是 operator

template<typename Container, typename = 
    std::enable_if_t<std::is_same_v<std::void_t<
        decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cbegin)),
        decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cend))>, void>
        && !std::is_same_v<std::string, Container>>>
std::ostream& operator<<(std::ostream& out, const Container &vec)
{
    std::cout << "[ ";
    for(const auto& t: vec){
        std::cout << t << " ";
    }
    std::cout << "] ";
    return out;
}

混乱,我知道,但是这样的语法糖可以用吗:)

另一个编辑:我之前发布的版本在使用 c-arrays 时出现了微妙的错误。这是一个更新的版本:

template<class...> constexpr bool true_t = true; // replacement of void_t

template<typename Container>
auto operator<<(std::ostream& out, Container&& vec) -> 
    std::enable_if_t<
        true_t<
            decltype(std::cbegin(vec)),
            decltype(std::cend(vec))
        >
        && !std::is_convertible_v<Container, std::string_view>,
        std::ostream&
    >
{
    std::cout << "[ ";
    for(const auto& t: vec){
        std::cout << t << " ";
    }
    std::cout << "] ";
    return out;
}

【讨论】:

  • 如果你混合容器,重要的是在实现之前声明所有函数(ADL 只会找到命名空间std 和来自T 的函数)。
  • 它还错过了逗号分隔符;-)
  • 我们能不能写一个函数模板来接受向量或者数组?
  • @patro 补充说。使用凌乱的函数模板魔法:)
  • @patro 好的。我确实使用了 SFINAE,使用 enable_if
【解决方案2】:

当你的第一个printContainerV2被启用时,!is_cont&lt;T&gt;::value就是true,这意味着你的T此时不再是container类型。在你的例子中,它们是int,所以你不能在int上调用std::begin,你应该直接返回std::to_string(value)

template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& value)
{
    return std::to_string(value);
}

Demo.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    相关资源
    最近更新 更多