【问题标题】:How to Generate Arbitrarily Nested Vectors in C++?如何在 C++ 中生成任意嵌套的向量?
【发布时间】:2018-04-18 06:02:30
【问题描述】:

在 C++ 中,是否可以生成深度(维度)等于用户定义输入的嵌套向量?例如,如果用户输入一个值为 2 的整数,那么一个人的程序可能会创建一个 vector< vector< vector<int> > > 类型的对象。显然,还有许多其他方法可以轻松地在 C++ 中实现类似的行为,但我纯粹对实际生成任意嵌套向量是否可能感兴趣。最初,我认为这很简单,但我的实现都以相当奇怪的方式失败了。

#include<iostream>
#include<vector>

using namespace std;

template<typename A> void vec_print(vector<A> in){
    cout << "{";
    for(typename vector<A> :: iterator i = in.begin(); i != in.end(); ++i){
        cout << *i << ", ";
    }
    cout << "}" << endl;
}

template<typename B> void vec_print(vector< vector<B> > in){
    cout << "{";
    for(typename vector< vector<B> > :: iterator i = in.begin(); i != in.end(); ++i){
        vec_print(*i); cout << ", ";
    }
    cout << "}" << endl;
}

template<typename T> auto generate(unsigned int d){
    if(d == 0){
        vector<T> in;
        return in;
    }
    return generate< vector<T> >(d - 1);
}

int main(){
    unsigned int d = 0;
    cin >> d;
    vec_print(generate<int>(d));
    return 0;
}

最初,我认为这可能有效,但由于我对 C++ 编译器如何处理模板函数的理解,我对此颇有怀疑。使用带有 --std=c++11 标志的 g++ 编译器,在崩溃之前,g++ 编译器递归地发出错误,通知我函数返回类型推导仅在 C++17 规范下可用。尝试使用--std=c++17 标志编译此程序不会导致任何错误,但编译器只是崩溃了。我怀疑编译器试图生成无限数量的,或者可能是 2^31 个模板函数,尽管我希望它能够处理这个问题,并发出无限模板函数生成的错误警告,而不是静默死亡。

【问题讨论】:

  • 您认为如何访问嵌套向量?我认为,一个是为树状结构创建类,例如一个基类 Vec,一个类 VecInt: Vec 和一个成员 vector&lt;int&gt;,一个类 VecVec: Vec 和一个成员 vector&lt;Vec*&gt; 以及管理它的适当虚拟方法。
  • 编译器无法推断出generate&lt;int&gt;(d) 的类型。 (你也不能。)
  • @molbdnilo 编译器是否无法推断返回类型,它不应该输出错误通知我吗?无论如何,我完全知道所提供的代码无法编译。我只是从完全理论的、非实践的角度询问是否可以在 C++ 中生成用户定义维度的 std::vector 容器。我虽然提供的代码可能会增加我所要求的内容的清晰度。在用户输入之前,是的,无法推断返回类型。我同意了。
  • @Scheff 访问任意嵌套向量的元素可以通过递归重载模板函数来实现,例如我的代码中的 vec_print 函数。我已经使用这个函数来访问过去项目的各种维度的 std::vectors。
  • 是的,它应该抱怨。崩溃是一个编译器错误,您应该报告它。但是类型不能依赖于运行时值。

标签: c++ c++11 templates vector c++17


【解决方案1】:

标准设置了嵌套模板实例化深度 (1024) 的限制,符合程序不应超过。但是,不需要实施来强制或诊断此限制或任何其他实施限制。它可能无法编译任何“太大”的程序。

gcc 和 clang 都可以使用标志来设置用户定义的模板实例化深度限制。为了查看发生了什么,请使用

g++ -std-c++17 -ftemplate-depth-20 yourprogram.cpp >& gcc.log

并查看生成的日志文件有多大。然后将深度增加到 21 并再次执行此操作。多做几次,然后将你的发现推断为-ftemplate-depth-1024

当然,编译器崩溃是一个 QoI 问题,应该被视为一个错误。无论如何,您的程序不符合要求,因为它超出了实现数量。

如果要处理任意维数的向量,则不能使用递归模板。您必须求助于需要在运行时而不是在编译时设置维度的其他技术。

【讨论】:

  • 我认为,除了作为语言律师之外,它并没有超过标准深度,使其不符合标准;它是无限深的,这使得它不符合标准,这是一个稍微更基本的问题。
  • @Yakk 实现不会检测到递归没有终止。
  • @Yakk 我不确定我的代码是否有任何混淆,但递归模板“生成”函数至少在理论上可以保证终止,因为它需要一个无符号整数参数并递减这个输入直到它等于零。这不是无限递归的情况,除非您指的是我的代码在编译器中引发的无限递归。在这种情况下,是的,我认为这是问题所在。
  • @ConnorMcMonigle 模板递归忽略运行时状态。实例在unsigned int 之前生成。如果您将 unsigned int 作为模板非类型参数传递,则不会出现这种情况。
  • @ConnorMcMonigle 你不能谈论“generate 函数”,因为你的程序中没有这样的东西。 generate 不是函数。它是一个函数模板。这个模板不能被实例化(即变成一个实际的函数,比如generate&lt;int&gt;),因为为了完成一个实例化,一个实现必须先完成另一个实例化,这要求它先完成​​另一个实例化,这...跨度>
【解决方案2】:
struct element_t;

struct element_t {
  ~element_t() {}
  using element_p = std::shared_ptr<element_t>;
  using data_t = std::variant< std::vector<element_p>, int >;
  data_t data;
  element_t(element_t const&)=default;
  element_t(data_t in):data(std::move(in)) {}
  element_t()=default;
};

element_t::data_t generate( unsigned int x ) {
  if (x==0) return {unsigned{0}};
  auto ptr = std::make_shared<element_t>(generate(x-1));
  auto vec = std::vector<element_t::element_p>{ptr};
  element_t::data_t r(vec);
  return r;
}

测试代码:

void print( element_t::data_t const& in ) {
    std::visit(
        [](auto const& e)
        {
            if constexpr( std::is_same< decltype(e), int const& >{} ) {
                std::cout << e << "\n";
            } else {
                for (const auto& x:e) {
                    if (!x)
                    {
                        std::cout << "null\n";
                    }
                    else
                    {
                        std::cout << "nest\n";
                        print(x->data);
                        std::cout << "unnest\n";
                    }
                }
            }
        },
        in
    );
}
int main() {
    auto r = generate(10);
    print(r);
}

Live example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多