2015-10-13 118 views
3

我試圖做一個方法,從容器類型中挑選一個隨機元素,如std::vector。之前,我使用的是:從模板模板方法參數獲取類型

std::string pick_random(std::vector<std::string> c) { 
    int r = std::rand() % ids.size() + 1; 
    auto it = c.begin(); 
    std::advance(it, r); 
    return *it; 
} 

哪個,據我所知,工作正常。這並不是說它好,只是它似乎是

我很快就不得不做同樣的事情對另一個容器,所以我嘗試使用模板模板參數,使該方法一般:

template <template<typename element_t> container_t> 
element_t pick_random(container_t from) { /* ... */ } 

然而,這將引發一個錯誤:

element_t does not name a type 

我認爲我的意圖已經足夠清楚,但要重申它:我試圖獲取列表的元素類型。我可以有一個單獨的模板參數,但它不能正確推斷類型。我嘗試了各種不同的版本,但都沒有工作。

回答

3

更好的辦法是避免對類模板的任意限制。畢竟,爲什麼不能從原始數組中選取一個元素?爲了正確地命名的C++ 11的類型,我們必須得到一個不合格的呼叫begin,我們可以通過獲得的結果:

namespace detail { 
    using std::begin; 

    template <typename C> 
    auto adl_begin(C&& c) -> decltype(begin(std::forward<C>(c))) { 
     return begin(std::forward<C>(c)); 
    } 
} 
using detail::adl_begin; 

然後用推斷來自任意容器的element_type:

template <typename C> 
auto pick_random(C& container) -> decltype(*adl_begin(container)) 
{ /* rest as before */ } 

附註:以容器引用而非價值。

+0

任何機會我可以得到一個newb的解釋?我不是說我是一個人,但是......好吧,我是。 –

+0

@QPaysTaxes你試圖得到的是 - 給定一個容器,獲取值類型。值類型是'* begin(容器)'的結果 - 所以這只是直接使用它,而不是通過分解容器類型來嘗試「猜測」值類型。 – Barry

+0

哦,我明白了!謝謝:D –

2

如果您只使用標準庫容器,那麼您可以通過使用container_t::value_type來獲取存儲類型。

template <typename container_t> 
typename container_t::value_type pick_random(container_t& container) 
{ ... } 
+1

我認爲你的意思是'container_t&container',因爲'C'沒有在任何地方定義;除此之外,很好的答案,但是無論什麼煩人的原因,編寫這個特殊花哨的容器類型的小夥不包括': :value_type' –

6

container_t不是一種類型,container_t<T>是。

您可以使用以下方法:

template <template<typename, typename...> C, typename T, typename...Ts> 
T pick_random(const C<T, Ts...>& from); 

std::vector,你有分配器:std::vector<T, Alloc>

在C++ 14,你可以簡單地使用auto

template <typename C> 
auto pick_random(const C& from) { /* ... */ } 
+1

爲什麼OP的'element_t'使用不被識別爲一個類型? – jaggedSpire

5

我不認爲「模板模板參數」在這裏需要 你可以簡單地使用value_type從容器:

#include <iostream> 
#include <cstdlib> 
#include <ctime> 
#include <vector> 
#include <list> 

template <typename T> 
typename T::value_type pick_random(T& from) { 
    int r = std::rand() % from.size(); 
    auto it = from.begin(); 
    std::advance(it, r); 
    return *it; 
} 

int main() { 
    std::srand(std::time(0)); 

    std::vector<std::string> words {"the", "frogurt", "is", "also", "cursed"}; 
    std::list<int> numbers {1, 2, 3, 4, 5}; 

    std::cout << "words: " << pick_random(words) << std::endl; 
    std::cout << "numbers: " << pick_random(numbers) << std::endl; 
} 

value_type - the type of the values that can be obtained by dereferencing the iterator.

Source: http://en.cppreference.com/w/cpp/iterator/iterator_traits