2013-05-31 151 views
0

我想創造一個簡單的輔助算法,將填充的容器,如std::vector<T>,具有幾何級數(第一項是a,並且n個術語由下式給出a * pow(r, n-1),其中r是給定的比例);我創建了下面的代碼:使用模板的模板時失敗模板參數推導參數

#include<vector> 
#include<algorithm> 
#include<iostream> 

template<template <typename> class Container, typename T> 
void progression(Container<T>& container, T a, T ratio, size_t N) { 
    if(N > 0) {  
    T factor = T(1); 
    for(size_t k=0; k<N; k++) { 
     container.push_back(a * factor); 
     factor *= ratio; 
    } 
    } 
} 

int main() { 
    std::vector<double> r; 
    progression(r, 10.0, 0.8, static_cast<size_t>(10)); 

    for(auto item : r) { 
    std::cout<<item<<std::endl; 
    } 

    return 0; 
} 

這在嘗試編譯產生了以下錯誤:

$ g++ geometric.cpp -std=c++11 # GCC 4.7.2 on OS X 10.7.4 
geometric.cpp: In function ‘int main()’: 
geometric.cpp:18:52: error: no matching function for call to ‘progression(std::vector<double>&, double, double, size_t)’ 
geometric.cpp:18:52: note: candidate is: 
geometric.cpp:6:6: note: template<template<class> class Container, class T> void progression(Container<T>&, T, T, size_t) 
geometric.cpp:6:6: note: template argument deduction/substitution failed: 
geometric.cpp:18:52: error: wrong number of template arguments (2, should be 1) 
geometric.cpp:5:36: error: provided for ‘template<class> class Container’ 

鏘的錯誤消息是更加微妙:

$ clang++ geometric.cpp -std=c++11 # clang 3.2 on OS X 10.7.4 
geometric.cpp:18:3: error: no matching function for call to 'progression' 
    progression(r, 10, 0.8, 10); 
    ^~~~~~~~~~~ 
geometric.cpp:6:6: note: candidate template ignored: failed template argument deduction 
void progression(Container<T>& container, T a, T ratio, size_t N) { 
    ^
1 error generated. 

我本來期望的是使用模板模板參數我不僅能夠推導出容器,還能推導容器的value_type(在這種情況下爲T)。

所以,問題是:如何創建一個泛型函數,它將能夠推導出容器類型和值類型?

我相信我錯過了一些明顯的東西 - 我感謝您的耐心和幫助。

編輯(答案)

下面的代碼的行爲與預期:

#include<vector> 
#include<algorithm> 
#include<iostream> 

template<template <typename...> class Container, typename T, typename... Args> 
void progression(Container<Args...>& container, T a, T ratio, size_t N) { 
    if(N > 0) {  
    T factor = T(1); 
    for(size_t k=0; k<N; k++) { 
     container.push_back(a * factor); 
     factor *= ratio; 
    } 
    } 
} 

int main() { 
    std::vector<double> r; 
    progression(r, 10.0, 0.8, 10); 

    for(auto item : r) { 
    std::cout<<item<<std::endl; 
    } 

    return 0; 
} 

輸出:

10 
8 
6.4 
5.12 
4.096 
3.2768 
2.62144 
2.09715 
1.67772 
1.34218 

回答

5

第一個問題是,你忘記std::vector<>是一個類模板接受兩個模板參數(元素類型和分配器),而不是一個。第二模板參數有默認值的事實是無關緊要的,當你使用模板,模板參數:

template<template <typename, typename> class Container, typename T, typename A> 
//       ^^^^^^^^        ^^^^^^^^^^ 
void progression(Container<T, A>& container, T a, T ratio, size_t N) { 
//       ^^^^ 
// ... 
} 

請注意,這將使其無法通過,例如,中std::mapstd::unordered_map的一個實例第一個函數參數。因此,我的建議是放棄推斷,第一個參數是一個標準集裝箱的實例(標準集裝箱,只是沒有那麼均勻):

template<typename C, typename T, typename A> 
//  ^^^^^^^^^ 
void progression(C& container, T a, T ratio, size_t N) { 
//    ^^ 
// ... 
} 

你可能想要做的又是什麼來表達編譯時約束,也許通過static_assert,並基於自定義類型的特點,那C必須是一個標準集裝箱的一個實例。

或者,您可以使用可變參數模板as suggested by KerrekSB in his answer(但仍然不會阻止您傳入任何其他類型的模板實例,即使是非容器模板也是如此)。

第二個問題是在你調用模板的方式:

progression(r, 10, 0.8, 10); 

這裏,第二個參數的類型是int,而容器元素的類型是double。這會在執行類型推斷時混淆編譯器。或者把它這種方式:

progression(r, 10.0, 0.8, 10); 

或讓你的編譯器來推斷不同類型的第二個參數(可能SFINAE約束它是東西,可以轉換成元素類型)。

+0

謝謝!這解決了這個問題。 – Escualo

+0

我其實有這個答案的改進版本,但我的網絡連接斷開,我無法從我的手機進行編輯(它只是一團糟)。 –

+0

@阿列塔:很高興幫助! –

1

,這是失敗的原因是因爲你說的中間有兩個參數是相同的類型,當他們實際上不是。例如,中間是一個浮點數,而另一個是一個int。基本上你說a和比例是相同的類型,但在通話中它們是不同的類型

+0

我認爲你的意思是說中間是一個「雙」,而前面的那個也應該是這樣。 – chris

+0

看看我的編輯 - 這不是問題。即使我傳遞兩個'double'並將整數作爲size_t來投放,問題依然存在。 – Escualo

+0

@aaronman - 這實際上是一個問題 - 在我刪除第一個錯誤之後,你描述的錯誤確實出現了。 – Escualo

4

容器通常有很多模板參數。幸運的是,在可變參數模板一項特別條款,允許使用使用一包的任何具體數目的參數:(特殊條款的影響,這個工程即使Container是一個非可變參數模板)

template <template <typename...> class Container, typename ...Args> 
void foo(Container<Args...> const & c) 
{ 
    // ... 
} 

+0

這不是一個可變參數模板 – aaronman

+1

@aaronman的語法:從來沒有聲稱這是? –

+0

我從來沒有想過要這樣做。那很整齊。 – chris