Неоднозначная рекурсивная шаблонная функция - PullRequest
0 голосов
/ 08 мая 2018

В 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, но компилятор говорит, что вызов неоднозначен. Может ли кто-нибудь объяснить, почему? и как это исправить?

Примечание: я знаю, что вы не можете частично специализировать шаблонную функцию. Я пытаюсь имитировать поведение с перегрузкой.


EDIT

Похоже, проблема заключается в расширении C::SizeAtCompileTime. Когда я жестко кодирую эту константу, программа компилируется. Существует ли общее правило C ++, указывающее, почему это происходит? или это что-то специфическое для Эйгена?

1 Ответ

0 голосов
/ 08 мая 2018

Причина неоднозначности, с одной стороны, заключается в том, что у вас есть два шаблона функций, которые одинаково хороши в части частичного упорядочения. Когда аргумент шаблона первой перегрузки не является зависимым значением (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>());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...