2017-01-18 79 views
2

我試圖用C++實現差異分解公式,因爲它出現了here用可變參數模板在C++中實現差異化的差異

到目前爲止,我已經來到這個

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蘋果LLVM版本8.0.0(clang-800.0。 42.1)目標:x86_64的 - 蘋果darwin15.4.0線程模型: POSIX InstalledDir:/庫/開發商/ CommandLineTools在/ usr/bin中

有誰知道爲什麼它不工作,如何解決它

+0

似乎是一個可變參數必須是在爲了月底,使其工作 –

+0

這是正確的,一個可變參數必須是最後一個。你可以將它打包在一個元組中, – 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

有沒有辦法使用variadic模板來做到這一點? –

+0

@krvajal要求花括號可能是一個功能,而不是一個錯誤 - 它表明那裏有一個列表。你可以添加一個包裝函數來添加大括號,並使用可變模板。當然,可以一直使用可變參數模板,但它會更混亂,效率更低,靈活性更低 - 爲什麼? – Potatoswatter

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