具体专业化的通用容器的功能

时间:2016-09-21 11:03:38

标签: 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));

    return true;
}

这个给了我

  

'容器'不是模板

template<template<typename> class Container>

我明白了:

  

错误:没有匹配函数来调用'findpeaks(cv :: MatExpr,std :: vector&gt;&amp;)'

     

...

     

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

     

错误:模板参数数量错误(2,应为1)

致电代码:

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

QVERIFY(daf::findpeaks(m.t(), peaks));

我也尝试过这样的事情:

template<template< template<typename, typename> typename > class Container>
  

警告:ISO C ++禁止模板模板参数中的typename键;使用-std = c ++ 1z或-std = gnu ++ 1z [-Wpedantic]

还有一些错误......

5 个答案:

答案 0 :(得分:4)

std::vector有两个模板参数。

template<  
    class T,
    class Allocator = std::allocator<T>
> class vector;

QVector有一个。您可以使用variadic template

执行此操作
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;
}

答案 1 :(得分:4)

您确实需要Container成为课程模板吗?只需将其设为正常类型:

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

    return true;
}

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

答案 2 :(得分:2)

你可以做

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

您的问题是std::vector有2个模板参数,类型T和分配器。

但你可以做得更简单:

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

    return true;
}

答案 3 :(得分:1)

一般来说,你不应该指定。如果我写道:

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,所以我们可以解决这个问题。首先我们写一个metatrait,然后是一些特征。

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 1}}是一个lambda,应该在代码体中接近零变化。但在此之前,您可以进行更高级别的优化,例如以流方式处理sink的输出,或者将其提供给异步队列等。

答案 4 :(得分:1)

就像@ Barry的回答一样,我认为你不需要这里的模板模板参数。

然而,您似乎关心表达&#34;一个支持push_back的容器,其中包含一对...&#34;

我建议你在sfinae约束表达式中表达这个约束。即使您的参数明确要求std::pair位于某个类的第一个模板参数中,但它并不意味着它具有push_back函数,并且并不代表所谓的push_back函数std::pairpush_back作为参数。

函数的参数目前是表达模板类型应该能够做什么或应该做什么的坏方法。你必须等待这个概念。

与此同时,您可以在函数签名中使用sfinae约束,该约束明确表示您需要具有接受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; } 函数的类型:

first_t

template<typename T, typename...> using first_t = T; 可以这样实现:

decltype

对于存在的函数,findpeaks内的表达式必须有效。如果没有满足约束条件,编译器将尝试函数public static string ModifyPositions(string positionsInput, int displacement) { string input = "l=50; r=190; t=-430; b=-480"; var pattern = @"(t|b)=(-?\d+)"; var regex = new Regex(pattern); var matches = regex.Matches(input); foreach (Match match in matches) { input = input.Replace(match.Groups[2].Value, (int.Parse(match.Groups[2].Value) + displacement).ToString()); } return input; } 的其他重载。