2017-02-20 27 views
3

我想實現的是一個makeSet()函數,它接受三個參數,一對迭代器和一個轉換值的函數。在函數模板中使用lambda,不能推導出類型,makeSet()用例

一個用例可以創建從值序列一套,做轉型,例如,轉換std::map<K,V>std::set<std::pair<V,K>>.

客戶端代碼可能看起來像

auto s = makeSet(hash.begin(), hash.end(), 
    [](std::pair<int,int> x) { return std::make_pair(x.second, x.first); }); 

我當前的嘗試是跟着,

// (commented code are some other *failed* attempt). 
template <typename Iterator, 
     typename T = typename std::iterator_traits<Iterator>::value_type, 
     template<typename ... > class Monad, typename R > 
     // typename R, typename Monad = std::function<R(T)> > 
std::set<R> makeSet(Iterator first, Iterator last, Monad<R,T> f) { 
    std::set<R> res; 
    for (; first != last; ++first) res.insert(f(*first)); 
    return res; 
} 

但不幸的是不起作用。該問題看起來像沒有推斷出R.

有沒有解決方法或解決方法? 如果你能告訴我正確的做法,我將非常感激。

回答

9

lambda表達式的類型是未命名的類類型(其封閉類型),而不是std::function。因此,您無法從中推導出std::functionMonad

你最好的選擇是做標準庫做什麼,簡單地接受任何作謂語:

template < 
    class Iterator, 
    class UnaryFunction 
> 
auto makeSet(Iterator first, Iterator last, UnaryFunction f) -> std::set<decltype(f(*first))> 
{ 
    std::set<decltype(f(*first))> res; 
    for (; first != last; ++first) res.insert(f(*first)); 
    return res; 
} 

請注意您可能必須包裝在std::remove_referencedecltype和/或std::remove_cv覆蓋所有(或者,如@Yakk,std::decay所建議的)。

此外,爲避免重新發明車輪,您可能需要查看Boost.Range庫。

+0

哦,心愛的,有時挑剔decltype! – qeatzy

+0

範圍是指使用範圍對象而不是一對迭代器,類似於[this](https://ericniebler.github.io/std/wg21/D4128.html),或者其他不同的東西? – qeatzy

+1

我只是使用'std :: decay_t ',因爲'decay_t'使得類型適合存儲。第二點是你幾乎不應該推導出'std :: function'的類型; 'std :: function'是一個類型擦除類,類型演繹和類型擦除是相反的。推導出要擦除的類型是設計缺陷的標誌。你擦除,因爲你需要一個固定的類型在這裏;你推斷,因爲你知道這裏的確切類型。如果你知道確切的類型,999/1000次你不需要固定類型。 – Yakk

0

「你越想過管道,越容易阻止排水管。」 - Scotty,Star Trek III。

沒有必要像這樣過度設計模板功能。只需使用轉發引用,並讓C++ 17編譯器將所有內容都排除在外。

#include <set> 
#include <map> 
#include <utility> 
#include <type_traits> 

// (commented code are some other *failed* attempt). 
template <typename Iterator, typename Lambda> 
auto makeSet(Iterator first, Iterator last, Lambda &&f) { 

    typedef typename std::remove_reference<decltype(first->first)>::type const_first_t; 

    typedef typename std::remove_const<const_first_t>::type first_t; 

    typedef typename std::remove_reference<decltype(first->second)>::type second_t; 

    typedef std::pair<first_t, second_t> R; 


    std::set<R> res; 

    for (; first != last; ++first) res.insert(f(*first)); 
    return res; 
} 


void foo() 
{ 
    std::map<int, int> m; 

    std::set<std::pair<int, int>> s = 
     makeSet(m.begin(), m.end(), 
      [](const auto &x) 
      { 
       return std::make_pair(x.second, x.first); 
      }); 

} 
+1

'R'應該是'f(* first)'的衰變類型,而不是你在這裏寫的任何東西。 – Barry

+0

好點!雖然我寧願更簡潔的版本。這就是爲什麼我不能提出類似的解決方案,這太令人望而生畏。 – qeatzy

+0

正如@Barry所指出的那樣,更爲一般的R是需要的,儘管這個想法是相似的。 – qeatzy

相關問題