Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/136.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用可变模板和lambda函数的二进制搜索_C++_Templates_C++11_Binary_Variadic - Fatal编程技术网

C++ 使用可变模板和lambda函数的二进制搜索

C++ 使用可变模板和lambda函数的二进制搜索,c++,templates,c++11,binary,variadic,C++,Templates,C++11,Binary,Variadic,想想这个, struct Person { std::string name; Person (const std::string& n) : name(n) {} std::string getName(int, char) const {return name;} // int, char play no role in this // simple example, but let's suppose that they are neede

想想这个,

struct Person {
    std::string name;
    Person (const std::string& n) : name(n) {}
    std::string getName(int, char) const {return name;}  // int, char play no role in this
        // simple example, but let's suppose that they are needed.
} *Bob = new Person("Bob"), *Frank = new Person("Frank"), *Mark = new Person("Mark"),
    *Tom = new Person("Tom"), *Zack = new Person("Zack");

const std::vector<Person*> people = {Bob, Frank, Mark, Tom, Zack};
因此,模板函数
binarySearch
可以通用。我是通过以下方式实现的:

#include <iostream>
#include <string>
#include <vector>
#include <functional>

struct Person {
    std::string name;
    Person (const std::string& n) : name(n) {}
    std::string getName(int, char) const {return name;}  // int, char play no role in this
        // simple example, but let's supposes that they are needed.
} *Bob = new Person("Bob"), *Frank = new Person("Frank"), *Mark = new Person("Mark"),
    *Tom = new Person("Tom"), *Zack = new Person("Zack");

const std::vector<Person*> people = {Bob, Frank, Mark, Tom, Zack};

template <typename Container, typename Ret>
typename Container::value_type binarySearch (const Container& container, const Ret& value,
        std::function<Ret(const typename Container::value_type&, int, char)> f,
        std::function<bool(const Ret&, const Ret&)> comp,
        typename Container::difference_type low, typename Container::difference_type high,
        int n, char c) {
    if (low > high)
        std::cout << "Error!  Not found!\n";
    const typename Container::difference_type mid = (low + high) / 2;
    const Ret& r = f(container[mid], n, c);
    if (r == value)
        return container[mid];
    if (comp(r, value))
        return binarySearch (container, value, f, comp, mid + 1, high, n, c);
    return binarySearch (container, value, f, comp, low, mid - 1, n, c);
}

template <typename Container, typename Ret>
typename Container::value_type binarySearch (const Container& container, const Ret& value,
        std::function<Ret(const typename Container::value_type&, int, char)> f,
        std::function<bool(const Ret&, const Ret&)> comp, int n, char c) {
    return binarySearch (container, value, f, comp, 0, container.size() - 1, n, c);
}

int main() {
    const Person* person = binarySearch<std::vector<Person*>, std::string>
        (people, "Tom", &Person::getName,
        [](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a');
    std::cout << person->getName(5,'a') << '\n';  // Tom
}
通用条款4.9.2:

[Error] no matching function for call to 'binarySearch(std::vector<Person*>&, const char [4], main()::__lambda0, main()::__lambda1, int, char)'
template argument deduction/substitution failed:
[Note] 'main()::__lambda0' is not derived from 'std::function<std::basic_string<char>(Person* const&, Args ...)>'
[Error]调用“binarySearch(std::vector&,const char[4],main():_lambda0,main():_lambda1,int,char)”时没有匹配的函数
模板参数扣除/替换失败:
[注意]“main()::_lambda0”不是从“std::function”派生的
更新: 在研究了雅克的解决方案后,我将我的解决方案改编为以下内容(使用更多的第一原则,而不是std::equal_range):

#包括
#包括
模板
typename容器::值\类型binarySearchRandomAccessIterator(常量容器和容器,T&&值,比较器和比较,typename容器::差异\类型低,typename容器::差异\类型高){
如果(低>高)
{std::cout 0){//使用迭代器执行二进制搜索。
它=第一;
步骤=计数/2;
std::advance(it,step);//这是在O(step)时间内完成的,因为ForwardIterator不是一个随机访问迭代器(否则它是在固定时间内完成的)。但好消息是,每次循环迭代,“step”都会变小一半。
const auto&t=compare.function(*it);//使用“const t&t”不会编译。
根据compare.comparator,如果(compare.comparator(t,value)){/'t'小于'value',则在上半部分搜索。
first=++it;//因此first将移动到超过中间点的位置,我们从那里开始搜索。
计数-=步数+1;//计数减半加1。
}
根据compare.comparator,else//'t'大于'value',因此保留在下半部分。
计数=步长;//“计数”和“步长”都减少一半。
}
if(比较函数(*first)!=值)
std::cout::type
binarySearch(const容器和容器,T&&value,Comparator&&compare={}){
在我看来,std::cout

Person* person = binarySearch (people, "Tom",
  [](Person* p, int n, char c) {return p->getName(n,c);},
 [](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a');
identity>
您的代码将开始编译(因为
Args…
是在其他地方推导的)。
identity
阻止对该参数进行模板类型推导,因此编译器不再尝试,而是从其他参数推断类型

然而,这并不是一个好的解决方案

一个更好的解决方案是将
f
c
的类型设置为
f
c
——根本不要将它们设置为
std::function
s。这消除了无意义的类型擦除开销,也消除了对
标识的需求

这仍然不是一个好的解决方案,因为您的模板函数可以做很多事情,但很少能做得很好。相反,请将您的操作分解为更简单的问题,然后将它们组合在一起:


首先,我们已经有了
std::equal_range
,这将是一个比您可能编写的任何一个更好的二进制搜索。编写一个返回单个元素并使用容器的函数似乎是合理的,因为使用迭代器很烦人

为了实现这一点,我们首先编写一些基于范围的样板:

namespace adl_aux {
  using std::begin; using std::end;
  template<class R>
  auto adl_begin(R&&)->decltype(begin(std::declval<R>()));
  template<class R>
  auto adl_end(R&&)->decltype(end(std::declval<R>()));
}
template<class R>
using adl_begin = decltype(adl_aux::adl_begin(std::declval<R>));
template<class R>
using adl_end = decltype(adl_aux::adl_end(std::declval<R>));

template<class R>using iterator_t = adl_begin<R>;
template<class R>using value_t = std::remove_reference_t<decltype(*std::declval<iterator_t<R>>())>;
template<class R, class T, class F=std::less<T>>
value_t<R>* bin_search( R&& r, T&& t, F&& f={} ) {
  using std::begin; using std::end;
  auto range = std::equal_range( begin(r), end(r), std::forward<T>(t), std::forward<F>(f) );
  if (range.first==range.second) return nullptr;
  return std::addressof( *range.first ); // in case someone overloaded `&`
}
它返回一个指向排序下元素
t
的指针
f
假定
R
在其下排序(如果存在),否则
nullptr

下一部分是您的订单:

[](Person* p, int n, char c) {return p->getName(n,c);},
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a'
如果您真的需要在一行上进行绑定,请直接进行绑定

下一步,我们需要订购人

template<class F, class C>
struct order_by_t : private F, private C {
  F const& f() const { return *this; }
  C const& c() const { return *this; }
  template<class T>
  auto f(T&&t)const
  ->decltype( std::declval<F const&>()(std::declval<T>()) )
  {
    return f()(std::forward<T>(t));
  }
  template<class T, class... Unused> // Unused to force lower priority
  auto f(T&&t, Unused&&... ) const
  -> std::decay_t<T>
  { return std::forward<T>(t); }
  template<class Lhs, class Rhs>
  bool operator()(Lhs&& lhs, Rhs&& rhs) const {
    return c()( f(std::forward<Lhs>(lhs)), f(std::forward<Rhs>(rhs)) );
  }
  template<class F0, class C0>
  order_by_t( F0&& f_, C0&& c_ ):
    F(std::forward<F0>(f_)), C(std::forward<C0>(c_))
  {}
};
template<class C=std::less<>, class F>
auto order_by( F&& f, C&& c={} )
-> order_by_t<std::decay_t<F>, std::decay_t<C>>
{ return {std::forward<F>(f), std::forward<C>(c)}; }
现在是符合您要求的
Person const*
s上的订单

然后我们将其输入到
bin\u search

auto ordering = order_by(
  [](int n, char c){
    return [n,c](Person const* p)
    ->decltype(p->getName(n,c)) // SFINAE enabled
    {return p->getName(n,c);}
  }(5,'a'),
  [](const std::string& x, const std::string& y) {return x.compare(y) < 0;}
);
Person*const* p = bin_search( people, "Tom", ordering );
是丑陋的,可以替换为:

  [](Person const* p)
    ->decltype(p->getName(5,'a')) // SFINAE enabled
    {return p->getName(5,'a');}
另外,由于lambda的参数检查已经足够了,我们可以删除SFINAE显式返回类型:

  [](Person const* p)
    {return p->getName(5,'a');}
我们完成了

甚至:

Person*const* p = bin_search( people, "Tom",
  order_by( [](Person const* p) {return p->getName(5,'a');} )
);
看起来不那么难看,不是吗

哦,还有:

using std::literals;
Person*const* p = bin_search( people, "Tom"s,
  order_by( [](Person const* p) {return p->getName(5,'a');} )
);
可能具有更好的性能,因为它可以避免在每次比较时重复构造
std::string(“Tom”)
。类似地,返回
std::string常量的
getName
(如果可能)也可以提高性能。“投影lambda”可能必须具有
->decltype(自动)
,实现第二次提升

我在上面使用了一些C++14。可以将
std::remove\u reference\u t
(和类似的)别名替换为
typename std::remove\u reference::type
,或者您可以编写自己的
\u t
别名。在C++11中,使用
decltype(auto)
的建议可以替换为
decltype(返回表达式)


order\u by\u t
使用继承来存储
F
C
,因为它们可能是空类,所以我想利用空基优化。

我不清楚您想要完成什么。一个通用的二进制搜索函数,它接受容器并查找lambda函数和一个值。当你说不能用Args…Args替换int,char时,你真的在函数的模板参数列表中包含了可变模板参数吗?你得到了什么错误?获取类型为
std::function
的函数参数几乎从来都不是你真正想要的。
order_by(
  [](int n, char c){
    return [n,c](Person const* p)
    ->decltype(p->getName(n,c)) // SFINAE enabled
    {return p->getName(n,c);};
  }(5,'a'),
  [](const std::string& x, const std::string& y) {return x.compare(y) < 0;}
}
auto ordering = order_by(
  [](int n, char c){
    return [n,c](Person const* p)
    ->decltype(p->getName(n,c)) // SFINAE enabled
    {return p->getName(n,c);}
  }(5,'a'),
  [](const std::string& x, const std::string& y) {return x.compare(y) < 0;}
);
Person*const* p = bin_search( people, "Tom", ordering );
  [](int n, char c){
    return [n,c](Person const* p)
    ->decltype(p->getName(n,c)) // SFINAE enabled
    {return p->getName(n,c);}
  }(5,'a'),
  [](Person const* p)
    ->decltype(p->getName(5,'a')) // SFINAE enabled
    {return p->getName(5,'a');}
  [](Person const* p)
    {return p->getName(5,'a');}
auto ordering = order_by(
   [](Person const* p)
     {return p->getName(5,'a');}
);
Person*const* p = bin_search( people, "Tom", ordering );
Person*const* p = bin_search( people, "Tom",
  order_by( [](Person const* p) {return p->getName(5,'a');} )
);
using std::literals;
Person*const* p = bin_search( people, "Tom"s,
  order_by( [](Person const* p) {return p->getName(5,'a');} )
);