使用可变参数模板实现C ++中的差异

时间:2017-01-18 04:08:25

标签: c++ c++11 templates variadic-functions

我正在尝试在C ++中实现划分的差异公式,因为它显示为here

到目前为止,我已经来了这个

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);

};

它编译得很好但是当它尝试使用它时,例如像这样

 double r = divdiff([](double x) { return 2 * x; }, 1.0, 2.0, 3.0);

我收到以下错误

note: candidate function not viable: requires 3 arguments, but 4 were provided
T divdiff(F f, T tstart, Args... t, T tend) {``

我的编译器是gcc

  

配置: - prefix = / Library / Developer / CommandLineTools / usr   --with-gxx-include-dir = / usr / include / c ++ / 4.2.1 Apple LLVM 8.0.0版(clang-800.0.42.1)目标:x86_64-apple-darwin15.4.0线程模型:   posix InstalledDir:/ Library / Developer / CommandLineTools / usr / bin

有谁知道它为什么不起作用以及如何解决它

2 个答案:

答案 0 :(得分:2)

template<typename F, typename T, typename... Args>
T divdiff(F f, T tstart, Args... t, T tend)

由于Args... t不在参数列表的末尾,因此不会推导出它。这种推论不允许部分简化语言规则,部分是为了帮助保持程序简单(并防止自己在脚下射击。)你可以明确指定Args ...divdiff<F, double, double>,但随后为递归要求删除最后一个double

在任何情况下,可变参数模板方法都会遭受模板膨胀,并且效率低下,因为参数列表可能会被每个函数调用复制。由于序列的元素应该都是相同的类型,因此请考虑使用迭代器。然后,您可以使用std::initializer_list为基于数组的可迭代序列添加方便重载。

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

答案 1 :(得分:0)

这是标准的,元组解包的递归解决方案。 我改变了你的lambda,因为线性函数在这里有点无聊。

#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';
}