Вывод типа аргумента шаблона из типа std:: function return с лямбдой
Сначала я использую С++ 11 (и моя тема отстой).
То, что я пытаюсь сделать, это написать общую функцию шаблона, которая реализует что-то обычно называемое sort_by
на других языках программирования. Это предполагает вычисление произвольного критерия для каждого члена диапазона ровно один раз, а затем сортировку этого диапазона в соответствии с этими критериями. Такой критерий не должен быть POD, все, что он должен быть, менее чем сопоставимо. Для вещей, для которых std::less
не работает, вызывающий должен быть в состоянии предоставить свой собственный функтор сравнения.
Я успешно написал указанную функцию, которая использует следующую подпись:
template< typename Tcriterion
, typename Titer
, typename Tcompare = std::less<Tcriterion>
>
void
sort_by(Titer first, Titer last,
std::function<Tcriterion(typename std::iterator_traits<Titer>::value_type const &)> criterion_maker,
Tcompare comparator = Tcompare()) {
}
Его можно использовать, например. например:
struct S { int a; std::string b; double c; };
std::vector<S> s_vec{
{ 42, "hello", 0.5 },
{ 42, "moo!", 1.2 },
{ 23, "fubar", 0.2 },
};
sort_by1< std::pair<int, double> >(
s_vec.begin(), s_vec.end(),
[](S const &one_s) { return std::make_pair(one_s.a, one_s.c); }
);
Что мне не нравится в этом подходе, так это то, что я должен сам предоставить аргумент Tcriterion
, потому что компилятор не может вывести этот тип из выражения лямбда. Поэтому это не работает:
sort_by1(s_vec.begin(), s_vec.end(), [](S const &one_s) { return std::make_pair(one_s.a, one_s.c); });
clang 3.1 и gcc 4.7.1 оба коры на этом (gcc 4.7.1 даже лает на код выше, поэтому я думаю, я действительно делаю что-то неправильно здесь).
Однако, если я сначала назначу лямбда на std::function
, тогда по крайней мере clang 3.1 может вывести аргумент, то есть это работает:
typedef std::pair<int, double> criterion_type;
std::function<criterion_type(S const &)> criterion_maker = [](S const &one_s) {
return std::make_pair(one_s.a, one_s.c);
};
sort_by1(s_vec.begin(), s_vec.end(), criterion_maker);
Итак, мои вопросы: как мне изменить свою сигнатуру функции, чтобы мне не нужно было указывать один аргумент? И (возможно, связанный), как бы исправить мой пример, чтобы он работал с gcc?
Ответы
Ответ 1
Не используйте std::function
в тандеме с выводом аргумента шаблона. На самом деле, вероятно, нет причин использовать std::function
в списке аргументов функции или функции. Чаще всего вам не следует использовать std::function
; это очень специализированный инструмент, который очень хорош в решении одной конкретной проблемы. В остальное время вы можете обойтись без него.
В вашем случае вам не нужен вывод аргумента шаблона, если вы используете полиморфный функтор для заказа вещей:
struct less {
template<typename T, typename U>
auto operator()(T&& t, U&& u) const
-> decltype( std::declval<T>() < std::declval<U>() )
{ return std::forward<T>(t) < std::forward<U>(u); }
// operator< is not appropriate for pointers however
// the Standard defines a 'composite pointer type' that
// would be very helpful here, left as an exercise to implement
template<typename T, typename U>
bool operator()(T* t, U* u) const
{ return std::less<typename std::common_type<T*, U*>::type> {}(t, u); }
};
Затем вы можете объявить:
template<typename Iter, typename Criterion, typename Comparator = less>
void sort_by(Iter first, Iter last, Criterion crit, Comparator comp = less {});
и comp(*ita, *itb)
будут поступать правильно, а также comp(crit(*ita), crit(*itb))
или что-либо еще, если это имеет смысл.
Ответ 2
Как насчет чего-то вроде этого:
template< typename Titer
, typename Tmaker
, typename Tcompare
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker,
Tcompare comparator)
{
typedef decltype(criterion_maker(*first)) Tcriterion;
/*
Now that you know the actual type of your criterion,
you can do the real work here
*/
}
Проблема заключается в том, что вы, очевидно, не можете использовать по умолчанию для компаратора, но вы можете легко преодолеть это, предоставляя перегрузку, которая не принимает компаратора и заполняет внутри std::less
внутренне.
Чтобы сделать это, как вы изначально предложили, компилятор должен был бы "инвертировать" процесс создания шаблона. То есть для данного экземпляра std:: function < > , какой параметр я должен предоставить в качестве результата для его получения. Это "выглядит" легко, но это не так!
Ответ 3
Вы также можете использовать что-то вроде этого.
template< typename Titer
, typename Tmaker
, typename TCriterion = typename
std::result_of
<
Tmaker
(
decltype(*std::declval<Titer>())
)
>::type
, typename Tcompare = std::less<TCriterion>
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker, Tcompare comparator = Tcompare())
{
}
http://liveworkspace.org/code/0aacc8906ab4102ac62ef0e45a37707d