В общем, вы не должны указывать. Если бы я писал:
struct my_thing {
void push_back(std::pair<int, double> const&) {}
};
я не должен быть в состоянии передать my_thing
findpeaks
вашему?
Внутри функции абсолютно нет необходимости в шаблоне template<class...>class Container
, поэтому его перенаправление в интерфейсе является превышением.
Что вам нужно - это раковина (теоретическая раковина на графе - это раковина, в которой вещи втекают и не вытекают из нее), которая потребляет пары int, double
. В идеале вы хотите пройти в контейнер без дополнительного шаблона.
template<class T>
struct sink:std::function<void(T)> {
using std::function<T>::function;
// more
};
теперь ваша функция выглядит следующим образом:
bool findpeaks(cv::Mat &m, sink<std::pair<int, double>const&> peaks) {
// do stuff
peaks(std::make_pair(1, 1.0));
return true;
}
и в качестве бонуса, вы можете поместить его в CPP файл вместо заголовка. (Расходы на доставку std::function
скромны).
Это требует, чтобы вы обернуть второй параметр вверх на месте вызова:
std::vector<std::pair<int, double>> v;
if(findpeaks(matrix, [&](auto&& e){v.push_back(decltype(e)(e));}) {
// ...
, которые вы не могли бы. Поскольку мы не использовали голый std::function
, а вместо этого sink
, мы можем обойти это. Сначала мы пишем метатрайт, затем некоторые черты.
namespace details {
template<template<class...>class Z, class alwaysvoid, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
Это мета-признак, который позволяет нам писать другие черты.
template<class C, class X>
using push_back_result = decltype(std::declval<C>().push_back(std::declval<X>()));
template<class C, class X>
using can_push_back = can_apply< push_back_result, C, X >;
теперь у нас есть признак can_push_back
, который true_type
тогда и только тогда, когда вы можете вытолкнуть X
в контейнер C
.
теперь увеличивают мойку:
template<class T, class Base=std::function<void(T)>>
struct sink:Base {
using Base::Base;
template<class C,
std::enable_if_t< can_push_back< C&, T >{}, int> =0
>
sink(C& c):
Base([&](auto&& t){ c.push_back(decltype(t)(t)); })
{}
sink()=default;
sink(sink const&)=default;
sink(sink &&)=default;
sink& operator=(sink const&)=default;
sink& operator=(sink &&)=default;
};
Этот недавно дополненной sink
теперь может быть передан контейнер, который поддерживает .push_back(T)
и автоматически записывает функцию, которая решает эту проблему для вас.
std::vector<std::pair<int, double>> v;
if(findpeaks(matrix, v) {
// ...
Это просто работает.
Мы можем сделать то же самое для контейнеров, которые поддерживают .insert(T)
, и после этого мы можем использовать std::set<T>
или даже std::map<int, double>
как раковина для вашего алгоритма:
std::set<std::pair<int, double>> s;
if(findpeaks(matrix, s) {
// ...
std::map<int, double> m;
if(findpeaks(matrix, m) {
// ...
Наконец, это также поддерживает насмешливый. Вы можете написать тестовый приемник, который поможет вам самостоятельно протестировать ваш findpeaks
.
Я нахожу, что понятие sink
используется достаточно часто, поскольку наличие типа sink
, который поддерживает такие вещи, делает мой код более четким и уменьшает его зависимость от любого вида контейнера.
Производительность разумная, стоимость стирания типа в std:function
скромна. если вам действительно нужно улучшить производительность, заменив sink<X>
на sink<X, F>
, где F
является свободным параметром, и записывает make_sink
, который создает sink
, где Base
- это лямбда, должна работать с почти нулевыми изменениями в тексте кода. Но прежде чем вы это сделаете, вы можете работать на более высоких уровнях оптимизации, например, если вывод в sink
обрабатывается потоковым образом или подается в очередь асинхронизации или тому подобное.
«тот же тип» показан не тип. Это функция. 'template class Container>' не является допустимым объявлением шаблона. Ваш вопрос непонятен. –
@SamVarshavchik Да, объявление шаблона недействительно, вопрос заключается в том, чтобы найти допустимый. «Тот же тип» не относится к функции, «алгоритм, который должен работать с» относится к функции. «тот же тип» относится к std :: pair. Значение любого контейнера этих пар, который поддерживает push_back. –