You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何打印n维C++ STL嵌套容器,实现类似numpy的输出格式?

C++递归打印任意维度嵌套容器(模拟numpy风格输出)

我需要将以vector嵌套vector、std::array嵌套std::array形式表示的张量按照numpy的打印风格输出。我正在学习C++元编程,想通过函数模板递归遍历任意维度的嵌套容器,返回可直接用cout输出的字符串。

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.])
>>> 

我此前参考了Stack Overflow上的问题《Print simply STL vectors of vectors recursively in C++》的已采纳回答,该方案对二维vector可以正常工作,但我将printContainerV2内部调用的printContainer改为printContainerV2以尝试支持三维vector时,出现编译报错。

问题代码

#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: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.

问题修复方案

错误根因

你的SFINAE分支逻辑写反了:

  • !is_cont<T>::value匹配的是int/float这类基础数值类型,不是容器,你在这个分支里调用std::begin遍历当然会报错,int本身没有迭代器接口。
  • 另外你现有的is_cont仅支持vector,还需要补充std::array的特化,才能满足你要支持array嵌套的需求。

修复后的完整代码

#include <iostream>
#include <iterator>
#include <vector>
#include <array>
#include <string>
#include <type_traits>

// 判断是否是容器的最后一个元素
template <typename Iter, typename Cont>
bool isLast(Iter iter, const Cont& cont)
{
    return (iter != cont.end()) && (std::next(iter) == cont.end());
}

// 类型萃取:判断是否是可遍历容器(vector/array)
template <typename T>
struct is_cont {
    static const bool value = false;
};
// vector特化
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
    static const bool value = true;
};
// array特化
template <typename T, size_t N>
struct is_cont<std::array<T,N> > {
    static const bool value = true;
};

// 分支1:不是容器(基础数值类型),直接转字符串返回
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& val)
{
    return std::to_string(val);
}

// 分支2:是容器,递归遍历子元素拼接字符串
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) {
        str += printContainerV2(*it);
        if (!isLast(it, container)) {
            str += ", ";
        }
    }
    str += "]";
    return str;
}

// 外层包装,模拟numpy的array前缀
template<typename T>
void printNumpyStyle(T const& t) {
    std::cout << "array(" << printContainerV2(t) << ")" << std::endl;
}

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::array<std::array<int,2>,3> arr = {1,2,3,4,5,6};

    printNumpyStyle(A);
    printNumpyStyle(M);
    printNumpyStyle(m3);
    printNumpyStyle(arr);
    return 0;
}

运行输出示例

array([2.000000, 3.000000, 6.000000, 8.000000])
array([[2.000000, 3.000000, 6.000000, 8.000000], [2.000000, 3.000000, 6.000000, 8.000000]])
array([[[1.000000, 2.000000], [3.000000, 4.000000]], [[5.000000, 6.000000], [7.000000, 8.000000]], [[1.000000, 2.000000], [5.000000, 9.000000]]])
array([[1.000000, 2.000000], [3.000000, 4.000000], [5.000000, 6.000000]])

如果需要完全对齐numpy的缩进和换行效果,只需要在递归的时候额外传递当前维度的缩进层级参数,输出时对应补全空格即可,逻辑和上述递归框架完全一致。


内容的提问来源于stack exchange,提问作者patro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 23:36:03