【问题标题】:Print simply STL vectors of vectors recursively in C++在 C++ 中以递归方式简单地打印向量的 STL 向量
【发布时间】:2014-12-21 21:30:17
【问题描述】:

我使用了以下代码,这些代码非常适合使用 printContainer() 打印简单的 std::vector。
我现在想用 printContainerV2()
为嵌套容器扩展它 我曾尝试使用模板来确定类型是否为 stl 容器,但它似乎不是这样做的方法。

#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>
std::string printContainerV2(T const& container)
{
    std::string str = "{";
    for (auto it = std::begin(container); it != std::end(container); ++ it)
        if (isLast(it, container))
            if (is_cont<decltype(*it)>::value == true)
                str = str + printContainer(*it);
            else
                str = str + std::to_string(*it) + "}";
        else
            if (is_cont<decltype(*it))>::value == true)
                str = str + printContainer(*it);
            else
                str = str + std::to_string(*it) + ",";
    return str;
}
*/
int main()
{
    std::vector<int> A({2,3,6,8});
    std::vector<std::vector<int>> M(2,A);
    M[1][0] ++;

    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::vector 类型和最多一个嵌套级别 (std::vector>) 是可以的。我不确定它是否可以不费吹灰之力就通用...

【问题讨论】:

  • 很抱歉,如果它是重复的。感谢您指出漂亮的印刷品,但它似乎是一个很长的代码。我想知道我的代码错误以及为什么我不能简单地修改它以获得我想要的?

标签: c++ templates c++11


【解决方案1】:

添加 #include &lt;type_traits&gt; 标头并将您的 PrintContainerV2 替换为:

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

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

template <typename T, typename if_not_cont<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 if_cont<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 + printContainer(*it) + "}";
        else
                str = str + printContainer(*it) + ",";
    return str;
}

【讨论】:

  • 感谢您的直截了当的回答!
  • @coincoin,嘿伙计,随时!我会在一分钟内使它更具可读性。
  • 不应该 printContainerV2() 在里面调用 printContainerV2() 吗?另外,这个解决方案真的有效吗?
【解决方案2】:

您的解决方案不起作用的原因在这里:

        if (is_cont<decltype(*it)>::value == true)
            str = str + printContainer(*it);
        else
            str = str + std::to_string(*it) + "}"; // <====

即使您所做的检查是编译时静态的 - if 的两个分支仍然会被编译。编译器仍会尝试评估 std::to_string(std::vector&lt;T&gt; ) 并抱怨该函数不存在。我们需要做的是使用 SFINAE: Substitution Failure Is Not An Error。

这是我们用于容器的print 版本:

template <typename T>
std::enable_if_t<is_cont<T>::value, std::string>
print(const T& container)
{
    // mostly the same as your code from printContainer() here, except instead of
    // std::to_string(*it), call print(*it).

    std::string str = "{";
    for (auto it = std::begin(container); it != std::end(container); ++ it)
    {
        str += print(*it);

        if (isLast(it, container)) {
            str += '}';
        }
        else {
            str += ',';
        }
    }

    return str;
}

以及非容器版本:

template <typename T>
std::enable_if_t<!is_cont<T>::value, std::string>
print(const T& value)
{
    return std::to_string(value);
}

逻辑上与您在 printContainerV2() 中所做的相同,但这样else 分支 - to_string() 分支 - 不会为实际容器版本编译.

【讨论】:

    【解决方案3】:

    我修改了你的代码,终于有了可行的解决方案:

    #include <iostream>
    #include <iterator>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    template <class N>
    struct is_vector { static const int value = 0; };
    
    template <class N, class A>
    struct is_vector<std::vector<N, A> > { static const int value = 1; };
    
    struct container_true {};
    struct container_false {};
    
    template <typename T>
    std::string print(T const& container);
    
    template <typename T>
    std::string printContainer(T const& container, container_true)
    {
        std::string ret = "{";
        for (auto it = std::begin(container); it != std::end(container); ++it)
        {
            ret += "{";
            ret += print(*it);
            ret += "}";
        }
    
        ret += "}";
        return ret;
    }
    
    
    template <typename T>
    std::string printContainer(T const& container, container_false)
    {
        std::string ret;
        for (auto it = std::begin(container); it != std::end(container); ++it)
        {
            ret += to_string(*it) + ",";
        }
        return ret.erase(ret.size() - 1);;
    }
    
    template <typename T>
    std::string print(T const& container)
    {
        typename std::conditional<is_vector<T::value_type>::value, container_true, container_false>::type mys;
        return printContainer(container, mys);
    }
    
    int main()
    {
        std::vector<int> A({ 2, 3, 6, 8 });
        std::vector<std::vector<int>> M(2, A);
        M[1][0]++;
        std::vector<std::vector<std::vector<int>>> Z(2,  M);
    
    
        std::cout << print(A) << std::endl;
        std::cout << print(M) << std::endl;
        std::cout << print(Z) << std::endl;
    }
    

    编辑:更短的解决方案:

    #include <iostream>
    #include <vector>
    #include <string>
    
    template <class N>
    struct is_vector { static const int value = 0; };
    
    template <class N, class A>
    struct is_vector<std::vector<N, A> > { static const int value = 1; };
    
    template <typename T>
    std::enable_if_t< is_vector<typename T::value_type>::value, std::string>
    printContainer(T const& container)
    {
        std::string ret = "{";
        for (auto& a : container)
            ret += "{" + printContainer(a) + "}";
        return ret + '}';
    }
    
    template <typename T>
    std::enable_if_t< !is_vector<typename T::value_type>::value, std::string>
    printContainer(T const& container)
    {
        std::string ret;
        for (auto& a : container)
            ret += std::to_string(a) + ",";
        return ret.erase(ret.size() - 1);;
    }
    
    int main()
    {
        std::vector<int> A({ 2, 3, 6, 8 });
        std::cout << printContainer(A) << std::endl;
    }
    

    【讨论】:

    • 感谢您的解决方案!
    【解决方案4】:

    我认为我的解决方案是递归的。我可以打印任何嵌套向量。

    main.cpp

    #include "vector.h"
    
    int main(){
        std::vector<std::vector<std::vector<int>>> e = {{{1,2,3},{4,5,6},{7,8,9}},{{9,8,7},{6,5,4},{3,2,1}}};
        std::cout << e << std::endl;
        return 0;
    }
    

    “向量.h”

    #ifndef VECTOR_H
    #define VECTOR_H
    
    #include <vector>
    #include <iostream>
    
    template<typename T1>
    std::ostream& operator<<(std::ostream& stream, std::vector<T1> r){
        if(r.size() == 0){
            return stream;
        }
        else{
            stream << "(";
            for(int i = 0; i < r.size(); i++){
                if(i < (r.size() - 1)){
                    stream << r[i] << ", ";
                }
                else{
                    stream << r[i] << ")";
                }
            }
        }
        return stream;
    };
    
    #endif
    

    【讨论】:

    • 不是专家,但我确实发现其他解决方案有点多,您的解决方案非常直接且易于阅读。不确定将解决方案放在名为 vector.h 的标头中是否合适
    猜你喜欢
    • 2021-05-12
    • 1970-01-01
    • 1970-01-01
    • 2015-11-13
    • 2020-06-03
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    相关资源
    最近更新 更多