c++ - 有歧义的递归模板函数

标签 c++ c++11 templates eigen eigen3

在 C++11 中,我需要从 0,...,n 递归调用一个函数(其中 n 是编译时间常量)。这是问题的结构,似乎存在致命缺陷:

#include "Eigen/Dense"

template<size_t i>
struct Int {
};

template<size_t d, typename C, typename X>
constexpr X eval(const C &c, const X &x, const Int<d> &, const Int<C::SizeAtCompileTime - 1 - d> &) {
    return 1;
}

template<size_t d, typename C, typename X, size_t i>
constexpr X eval(const C &c, const X &x, const Int<d> &, const Int<i> &) {
    return x * eval(c, x, Int<d>(), Int<i + 1>());
}

int main() {
    const size_t d = 1;
    const Eigen::Matrix<double, 1, 7> c = Eigen::Matrix<double,1,7>::Zero();
    const double x = 5;
    eval(c, x, Int<d>(), Int<0>());
}

以及完整的错误信息:

/usr/bin/cmake --build /mnt/c/Dropbox/clion/recursion/cmake-build-debug --target recursion -- -j 4
Scanning dependencies of target recursion
[ 50%] Building CXX object CMakeFiles/recursion.dir/main.cpp.o
/mnt/c/Dropbox/clion/recursion/main.cpp: In instantiation of 'constexpr X eval(const C&, const X&, const Int<d>&, const Int<i>&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double; long unsigned int i = 4ul]':
/mnt/c/Dropbox/clion/recursion/main.cpp:14:20:   recursively required from 'constexpr X eval(const C&, const X&, const Int<d>&, const Int<i>&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double; long unsigned int i = 1ul]'
/mnt/c/Dropbox/clion/recursion/main.cpp:14:20:   required from 'constexpr X eval(const C&, const X&, const Int<d>&, const Int<i>&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double; long unsigned int i = 0ul]'
/mnt/c/Dropbox/clion/recursion/main.cpp:21:34:   required from here
/mnt/c/Dropbox/clion/recursion/main.cpp:14:20: error: call of overloaded 'eval(const Eigen::Matrix<double, 1, 7>&, const double&, Int<1ul>, Int<5ul>)' is ambiguous
     return x * eval(c, x, Int<d>(), Int<i + 1>());
                    ^
/mnt/c/Dropbox/clion/recursion/main.cpp:8:13: note: candidate: constexpr X eval(const C&, const X&, const Int<d>&, const Int<((C:: SizeAtCompileTime - 1) - d)>&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double]
 constexpr X eval(const C &c, const X &x, const Int<d> &, const Int<C::SizeAtCompileTime - 1 - d> &) {
             ^
/mnt/c/Dropbox/clion/recursion/main.cpp:13:13: note: candidate: constexpr X eval(const C&, const X&, const Int<d>&, const Int<i>&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double; long unsigned int i = 5ul]
 constexpr X eval(const C &c, const X &x, const Int<d> &, const Int<i> &) {
             ^
/mnt/c/Dropbox/clion/recursion/main.cpp:15:1: error: body of constexpr function 'constexpr X eval(const C&, const X&, const Int<d>&, const Int<i>&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double; long unsigned int i = 4ul]' not a return-statement
 }

我的理解是在最后一行,x * eval(c, x, Int<d>(), Int<i + 1>()); , 当 i+1 = n ,那么将选择返回 1 的第一个函数,但编译器说该调用不明确。有人可以解释为什么吗?以及如何解决这个问题?

注意:我知道您不能部分特化模板函数。我试图通过重载来模仿这种行为。


编辑

看来问题出在C::SizeAtCompileTime的扩展上.当我对该常量进行硬编码时,程序会编译。是否有通用的 C++ 规则说明为什么会这样?还是 Eigen 特有的东西?

最佳答案

造成歧义的原因一方面是您有两个函数模板,就偏序而言它们同样好。当第一个重载的模板参数不是依赖值(n 在您的原始问题中)时,部分排序可以很好地决定它。但是 C::SizeAtCompileTime 是相关的,因此部分排序不再能拯救我们。

不过,我们可以使用 SFINAE 自行解决。当 i 可能与我们发生冲突时,我们需要做的就是从重载集中删除第二个重载。这可以通过一个简单的 std::enable_if 来完成:

template<size_t d, typename C, typename X, size_t i>
constexpr auto eval(const C &c, const X &x, const Int<d> &, const Int<i> &)
  -> typename std::enable_if<i != C::SizeAtCompileTime - 1 - d, X>::type {
    return x * eval(c, x, Int<d>(), Int<i + 1>());
}

关于c++ - 有歧义的递归模板函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50233314/

相关文章:

C++11 使用 pow() 和 std::complex 舍入错误

c++ - 在 lambda [C++] 中捕获它的替代方法

c++11 可变参数模板编译失败

c++ - 类成员函数模板可以是虚拟的吗?

c++ - 为什么从 fscanf 读取了不正确的数据?

c++ - 带命令提示符的命名管道

c++ - 实现 union-find C++ 的段错误(核心转储)

c++ - 在 C++ 中选择两个纯虚函数之一

c++ - 如何在 Mac OSX 中获取真实的日历微秒时间(自 1970 年以来的纪元)?

使用 const 和 & 的 C++ 模板化函数包装器参数