如何基于数组值递归裁切Eigen Tensor?模板实例化过深问题求解
问题原因分析
你的递归实现触发模板实例化深度超限,核心原因是运行时条件无法让编译器提前终止模板递归:
- 每次调用
chipTensorRecursive时,TensorType的类型都会改变(chip()返回的是维度减1的TensorChipOp视图类型),编译器会为每个不同的TensorType生成新的模板实例。 - 由于
dim是运行时变量,编译器无法在编译阶段确定dim >= indices.size()的终止条件何时触发,会不断递归实例化模板,直到达到编译器的默认递归深度上限(比如900)。
你的递归思路本身方向没错,但没有利用编译时信息来控制递归终止,导致了不必要的模板爆炸。
可行解决方案
要解决这个问题,需要将维度处理逻辑放到编译时,让编译器能明确递归终止点。以下是两种实现方式:
方式一:利用编译时索引序列(C++14及以上)
通过std::index_sequence将维度索引转化为编译时常量,编译器可以准确控制递归深度:
#include <Eigen/Dense> #include <unsupported/Eigen/CXX11/Tensor> #include <iostream> #include <vector> #include <utility> // 递归终止:所有维度处理完成 template<typename TensorType, std::size_t... Is> auto chipTensorImpl(TensorType& tensor, const std::vector<int>& indices, std::index_sequence<Is...>) { return tensor; } // 递归处理每个维度 template<typename TensorType, std::size_t I, std::size_t... Is> auto chipTensorImpl(TensorType& tensor, const std::vector<int>& indices, std::index_sequence<I, Is...>) { if (indices[I] == -1) { // 跳过当前维度,处理下一个 return chipTensorImpl(tensor, indices, std::index_sequence<Is...>{}); } else { // 裁切当前维度,递归处理剩余维度 auto chipped = tensor.chip(indices[I], I); return chipTensorImpl(chipped, indices, std::index_sequence<Is...>{}); } } template<typename TensorType> auto chipTensor(TensorType& tensor, const std::vector<int>& indices) { static_assert(TensorType::NumDimensions == indices.size(), "Indices size must match tensor dimensions"); return chipTensorImpl(tensor, indices, std::make_index_sequence<TensorType::NumDimensions>{}); } int main() { Eigen::Tensor<double, 3> tensor(4, 3, 2); std::vector<int> indices = {1, -1, 1}; tensor.setValues({{{ 0, 1}, { 2, 3}, { 4, 5}}, {{ 6, 7}, { 8, 9}, {10, 11}}, {{12, 13}, {14, 15}, {16, 17}}, {{18, 19}, {20, 21}, {22, 23}}}); auto result = chipTensor(tensor, indices); std::cout << "Result: " << result << std::endl; return 0; }
方式二:C++17折叠表达式(更简洁)
利用折叠表达式一次性处理所有维度,避免显式递归:
#include <Eigen/Dense> #include <unsupported/Eigen/CXX11/Tensor> #include <iostream> #include <vector> #include <utility> template<typename TensorType, std::size_t... Is> auto chipTensorImpl(TensorType tensor, const std::vector<int>& indices, std::index_sequence<Is...>) { // 用折叠表达式逐个应用chip操作 return ((indices[Is] == -1 ? tensor : tensor.chip(indices[Is], Is)) ...); } template<typename TensorType> auto chipTensor(TensorType& tensor, const std::vector<int>& indices) { static_assert(TensorType::NumDimensions == indices.size(), "Indices size must match tensor dimensions"); return chipTensorImpl(tensor, indices, std::make_index_sequence<TensorType::NumDimensions>{}); } int main() { Eigen::Tensor<double, 3> tensor(4, 3, 2); std::vector<int> indices = {1, -1, 1}; tensor.setValues({{{ 0, 1}, { 2, 3}, { 4, 5}}, {{ 6, 7}, { 8, 9}, {10, 11}}, {{12, 13}, {14, 15}, {16, 17}}, {{18, 19}, {20, 21}, {22, 23}}}); auto result = chipTensor(tensor, indices); std::cout << "Result: " << result << std::endl; return 0; }
关于递归方案的合理性
你的初始递归思路是合理的,但没有结合编译时信息来控制递归终止。Eigen的Tensor操作都是基于模板的编译时多态,所有维度相关的逻辑最好放到编译时处理,这样既可以避免模板实例化爆炸,还能让编译器进行更多优化。
内容的提问来源于stack exchange,提问作者Yes
相关产品推荐
相关产品推荐

