2017-02-20 22 views
3

То, что я хочу достичь, является функцией makeSet(), принимающей три аргумента, пару итераторов и функцию, которая преобразует значение.использовать лямбда в функции шаблон, не могу вывести тип, makeSet() use case

Один случай использования может создавать набор из последовательности значений и не преобразование, например, преобразовать 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

Тип выражения лямбда - это неназванный тип класса (его тип закрытия), а не std::function. Поэтому вы не можете вывести std::function или Monad.

Лучше бы делать то, что стандартная библиотека делает, а просто принимать что-либо в качестве предиката:

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

Обратите внимание, что вы, возможно, придется обернуть decltype в std::remove_reference и/или std::remove_cv, чтобы охватить все (или, как предложено @Yakk, std::decay).

Кроме того, чтобы избежать повторного изобретения колеса, вы можете взглянуть на библиотеку Boost.Range.

+0

О, любимый и когда-то придирчивый деклатип !! – 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

«Чем больше вы перепутаете водопровод, тем легче остановить слив». - Скотти, Звездный путь 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