C++ 特定专门化的通用容器的功能

C++ 特定专门化的通用容器的功能,c++,templates,c++11,vector,generic-programming,C++,Templates,C++11,Vector,Generic Programming,我正在尝试编写一个算法,该算法应适用于包含相同类型的不同容器(std::vector,QVector): template<class Container> boolean findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) { // do stuff peaks.push_back(std::make_pair(1, 1.0)); retur

我正在尝试编写一个算法,该算法应适用于包含相同类型的不同容器(std::vector,QVector):

template<class Container>
boolean findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
    // do stuff
    peaks.push_back(std::make_pair(1, 1.0));

    return true;
}
模板
布尔FindPeak(cv::Mat&m、容器和峰值){
//做事
峰值。推回(std::make_pair(1,1.0));
返回true;
}
这个给我

“容器”不是模板

模板
我得到:

错误:调用“findpeaks(cv::MatExpr,std::vector>&)”时没有匹配函数

注意:模板参数扣除/替换失败:

错误:模板参数的数目错误(2,应该是1)

呼叫代码:

cv::Mat m(data, true);
std::vector<std::pair<int, double>> peaks;

QVERIFY(daf::findpeaks(m.t(), peaks));
cv::Mat m(数据,真);
std::向量峰;
QVERIFY(daf::findpeaks(m.t(),peaks));
我也试过这样的方法:

template<template< template<typename, typename> typename > class Container>
模板类容器>
警告:ISO C++禁止模板模板参数中的类型名键;使用-std=c++1z或-std=gnu++1z[-Wpedantic]

还有一些错误…

您可以这样做

template<template <typename ...> class Container>
bool findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
    // do stuff
    peaks.push_back(std::make_pair(1, 1.0));

    return true;
}
有两个模板参数


您真的需要
容器
作为类模板吗?只需将其设置为普通类型:

template<class Container>
boolean findpeaks(cv::Mat &m, Container& peaks) {
    // do stuff
    peaks.push_back(std::make_pair(1, 1.0));

    return true;
}
模板
布尔FindPeak(cv::Mat&m、容器和峰值){
//做事
峰值。推回(std::make_pair(1,1.0));
返回true;
}

这将允许您使用其他可能不是模板的容器。比如,
struct MySpecialPairContainer{…}

通常,您不应该过度指定。如果我写:

struct my_thing {
  void push_back( std::pair<int, double> const& ) {}
};
现在,您的函数如下所示:

bool findpeaks(cv::Mat &m, sink<std::pair<int, double>const&> peaks) {
  // do stuff
  peaks(std::make_pair(1, 1.0));

  return true;
}
你可能不喜欢。因为我们没有使用裸
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...>;
这个新增加的
sink
现在可以传递一个支持
的容器。push_back(T)
并自动编写一个函数来为您解决问题

std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, v ) {
  // ...
最后,这也支持模拟。您可以编写一个测试接收器,帮助直接对
findpeaks
算法进行单元测试

我发现
sink
的概念经常被使用,拥有支持这类东西的
sink
类型可以使我的代码更清晰,并减少它对任何一种容器的依赖性


就性能而言,
std:function
中的类型擦除成本适中。如果确实需要提高性能,可以将
sink
替换为
sink
,其中
F
是一个自由参数,编写
make_sink
创建
sink
,其中
Base
是一个lambda,应该可以在代码体中进行几乎零的更改。但在此之前,您可以进行更高级别的优化,比如以流式方式处理
sink
中的输出,或者将其馈送到异步队列,等等。

正如@Barry的答案所述,我认为这里不需要模板参数

然而,您似乎关心如何表达“一个支持
push_back
的容器,它有一对

我建议您用sfinae约束表达式来表示此约束。即使您的参数明确要求
std::pair
位于类的第一个模板参数中,也不意味着它具有
push_back
功能,也不意味着假定存在的
push_back
std::pair
作为参数

函数的参数目前是表示模板类型应该能够做什么或应该做什么的糟糕方式。你得等一等

同时,您可以在函数签名中使用sfinae约束,该约束明确表示您需要一个类型,该类型的成员
push_back
函数接受
std::pair

template<class Container>
auto findpeaks(cv::Mat &m, Container& peaks)
        // Using trailing return type
        -> first_t<bool, decltype(peaks.push_back(std::make_pair(1, 1.0)))>
        // Here's the constraint -^    that expression need to be valid
    {

    // do stuff
    peaks.push_back(std::make_pair(1, 1.0));

    return true;
}

要使函数存在,
decltype
中的表达式必须有效。如果未满足约束条件,编译器将尝试函数的其他重载
findpeaks

“显示的相同类型”不是类型。这是一个函数<代码>模板
不是有效的模板声明。您的问题不清楚。@SamVarshavchik是的,模板声明无效,问题是要找到一个有效的声明:)“相同类型”不指函数,“应使用的算法”指函数。“同一类型”指的是std::pair。表示那些对的任何容器,支持push_back。虽然这是一个更简单的解决方案,但我希望签名中有std::pair,以明确这是您得到的:)@benjaminamurer您可以用sfinae约束而不是函数参数来表示。@guillaumracicot mhh,我不知道该怎么做:)@benjaminamurer我已经写了一个答案来解释如何做。别忘了把这张(巴里的那张)也投上一票,因为它提出了很好的观点。别误会,这是一些很酷的东西。如果我写这篇文章是作为一个面向公众的通用图书馆的一部分,那可能是有意义的。但在这里,这是Waaay过度设计的。代码库简单地混合了std::vector和QVector,我不想提交一个。即使我这么做了,也不会太可怕。但是通过添加可变模板位,我添加了一行,调用者就不必在意了。@BenjaminMurer将
std::function
作为接收器。从标题中取出代码通常是值得的,而且胶水很小。在make_pair之后,您缺少一个右括号“)”。除此之外,我的奥秘模板魔术你的C++向导。现在应该已经修好了。这个概念将来可能会有帮助。
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;
}
std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, [&](auto&& e){v.push_back(decltype(e)(e));} ) {
  // ...
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 >;
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;
};
std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, v ) {
  // ...
std::set<std::pair<int, double>> s;
if(findpeaks( matrix, s ) {
  // ...
std::map<int, double> m;
if(findpeaks( matrix, m ) {
  // ...
template<class Container>
auto findpeaks(cv::Mat &m, Container& peaks)
        // Using trailing return type
        -> first_t<bool, decltype(peaks.push_back(std::make_pair(1, 1.0)))>
        // Here's the constraint -^    that expression need to be valid
    {

    // do stuff
    peaks.push_back(std::make_pair(1, 1.0));

    return true;
}
template<typename T, typename...>
using first_t = T;