Reputation: 1974
I am trying to implement in C++ the divided differences formula as it appears here.
So far I have come with this
template<typename F, typename T>
T divdiff(F f, T t1, T t2) {
return (f(t1) - f(t2)) / (t1 - t2);
};
template<typename F, typename T, typename... Args>
T divdiff(F f, T tstart, Args... t, T tend) {
return (divdiff(f, tstart, t...) - divdiff(f, t..., tend))/ (tstart - tend);
};
It compiles fine but when it try to use it for example like this
double r = divdiff([](double x) { return 2 * x; }, 1.0, 2.0, 3.0);
I got the following error
note: candidate function not viable: requires 3 arguments, but 4 were provided
T divdiff(F f, T tstart, Args... t, T tend) {``
My compilers is gcc
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 Apple LLVM version 8.0.0 (clang-800.0.42.1) Target: x86_64-apple-darwin15.4.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Does anyone knows why it does not work and how to fix it
Upvotes: 2
Views: 233
Reputation: 3107
Heres the standard, tuple-unpacking recursive solution. I changed your lambda, because linear functions are kinda boring here.
#include <iostream>
#include <utility>
#include <tuple>
// The base version of divdiff, ends recursion
template<typename F, typename T>
T divdiff(F f, T t0, T t1) {
return (f(t0) - f(t1)) / (t0 - t1);
}
// This divdiff overload takes a tuple and an index sequence
// The index sequence specifies which elements from the tuple will
// be unpacked as arguments for a divdiff call
template <typename F, typename T, std::size_t... Is>
auto divdiff(F f, T arg_tuple, std::index_sequence<Is...>) {
return divdiff(f, std::get<Is>(arg_tuple)...);
}
template<typename F, typename T, typename ...Ts>
T divdiff(F f, T t0, Ts ...right_args) {
// pack all arguments into a tuple
auto arg_tuple = std::make_tuple(t0, right_args...);
// make an index sequence whose size is one less than the
// current recursion's argument count
using next_index_sequence = std::make_index_sequence<sizeof...(Ts)>;
// get the value of the final argument in tn
auto tn = std::get<sizeof...(Ts)>(arg_tuple);
// Call divdiff, first using the tuple/sequence overload for the left
// side arguments.
// Then call it with the easily-obtained right side arguments.
return (divdiff(f, arg_tuple, next_index_sequence{})
- divdiff(f, right_args...)) / (t0 - tn);
}
int main() {
double r = divdiff([](double x) { return x * x * x; }, 1.0, 2.0, 3.0);
std::cout << r << '\n';
}
Upvotes: 0
Reputation: 137910
template<typename F, typename T, typename... Args> T divdiff(F f, T tstart, Args... t, T tend)
Since Args... t
is not at the end of the parameter list, it will not be deduced. Such deduction is not allowed partly to simplify the language rules, and partly to help keep programs simple (and prevent shooting oneself in the foot.) You could specify Args ...
explicitly like divdiff<F, double, double>
, but then for the recursive call it would be difficult to remove the last double
.
In any case, the variadic template approach suffers template bloat, and inefficiency as the argument list may be copied by each function call. Since the elements of the sequence should all be the same type, consider using iterators instead. Then you can add a convenience overload using std::initializer_list
for array-based iterable sequences.
template< typename F, typename bidirectional_iterator >
typename std::iterator_traits< bidirectional_iterator >::value_type
divdiff( F f, bidirectional_iterator first, bidirectional_iterator last ) {
bidirectional_iterator next = std::next( first );
bidirectional_iterator prev = std::prev( last );
auto diff = next == prev?
f( * first ) - f( * prev )
: divdiff( f, first, prev ) - divdiff( f, next, last );
return diff / ( * first - * prev );
}
template< typename F, typename T >
T divdiff( F f, std::initializer_list< T > il )
{ return divdiff( f, il.begin(), il.end() ); }
Demo.
Upvotes: 2