2017-01-18 6 views
2

C++で実装しようとしていますが、差分差分式はhereと表示されています。C++でvariadicテンプレートとの差分を実装

は、これまでのところ私は、付属しているこの

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版 - アップルdarwin15.4.0スレッドモデル: POSIXのInstalledDir:/ライブラリ/開発/ CommandLineToolsは/ usr/binに

それが動作しないと

+0

それは可変長引数が、それは正しいです –

+0

を動作させるために、終わりでなければならないと思われ、可変長引数は、最後の1でなければなりません。 – Danh

答えて

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

+0

実際にはかなりクールですが、私は '' 'divdiff(f、{a、b})' 'のように関数を呼び出さなければなりません。 –

+0

バリデリックテンプレートを使ってそれを行う方法はありますか? –

+0

@krvajal中括弧を必要とすることは機能ではなく、バグではなく、そこにリストがあることを示します。中括弧を追加するためにラッパー関数を追加することができます。これは、バリデーショナルテンプレートを使用します。確かに、variadicテンプレートは一貫して使用することは可能ですが、それは難読化され、効率が低く、柔軟性が低くなります - なぜですか? – Potatoswatter

0

標準的なタプルアンパック再帰的解法を得ます。 ラムダを変更しました。線形関数はちょっと退屈です。

#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'; 
} 
関連する問題