函子与比较器

时间:2019-07-12 06:57:33

标签: c++ c++11 comparator functor

我知道函子是一个函数对象,是结构定义中()运算符的重载。 同样,在算法中使用函子似乎很简单,只需调用此例程即可。

但是我无法理解比较器。为什么在模板参数中首先使用它们。

有人可以用map等STL中的模板实现来详细说明两者之间的区别吗?

编辑:

我正在寻找以下内容的答案

  1. 为什么需要比较器而不是函数对象(比较器在容器中更常见?)
  2. 传递比较器而不是函数对象的可能的实现方式(非STL,即C ++代码)

2 个答案:

答案 0 :(得分:7)

您对 functor 的定义是正确的-尽管该单词在标准语言本身中并不存在,所以人们的使用方式可能略有不同。

标准库中有许多函数或类模板,它们会带有某种可调用的对象-这可能是函子或指向函数的指针(实际上只是一个函数,而不是带有operator()的类)。

比较器是满足Compare requirements的类型的对象-即,可以用两件事调用并返回bool的函数或类对象,并且特别满足一些称为“ 严格弱排序”的数学要求。

从本质上讲,这意味着比较器是一个函子,您可以用来按正确的顺序放置一些数字。 (数字,std::stringCustomer,无论其他什么,只要有一种合理的,一致的方式将它们排序即可。)

因此使用函子的一个简单例子可能是:

void print(int i)
{
    std::cout << i << '\n';
}
// ...
std::for_each(std::begin(some_ints), std::end(some_ints), print);

但是,如果您想按其客户ID对某些Customer进行排序,则可以这样操作:

struct Customer {
    std::string surname;
    std::string given_name;
    std::uint64_t customer_id;
};
bool compareById(Customer const& first, Customer const& second)
    // this function meets the Compare requirements
{
    return first.customer_id < second.customer_id;
}
// ...
std::sort(std::begin(customers), std::end(customers), compareById);

假设您稍后要按客户的名称对客户进行排序-首先是姓,然后如果姓相同,则给定名称,您可以提供其他功能:

bool compareByName(Customer const& first, Customer const& second)
{
    // std::tie is an idiomatic way to correctly sort on multiple values
    return std::tie(first.surname, first.given_name)
                < std::tie(second.surname, second.given_name);
}
std::sort(std::begin(customers), std::end(customers), compareByName);

我正在努力创造一个示例,其中您需要将比较器作为一个类,但让我们假设您想将其所做的所有比较打印到日志文件中;那么该文件将需要由对象存储状态:

struct LoggingCustomerComparator {
    std::ostream& logFile;
    LoggingCustomerComparator(std::ostream& logFile) : logFile(logFile) {}
    bool operator()(Customer const& first, Customer const& second)
    {
        // assume we have an operator<< for Customer
        logFile << "Comparing: " << first << " and " << second << '\n';
        return first.customer_id < second.customer_id;
    }
};
// ...
using OrderId = std::uint64_t;
using LCC = LoggingCustomerComparator;
std::map<Customer, OrderId, LCC> latestCustomerOrder(LCC(std::clog));
//                          ^^^ type                 ^^^ construct object with the log file we want

上面的示例说明了如何使用带有函子或比较器的函数模板,但是如果您想编写这样的函数模板怎么办?让我们以标准库算法的样式实现Bogosort

template <typename RandIt, typename Comp>
void bogosort(RandIt first, RandIt last, Comp comp)
{
    std::random_device rd;
    std::mt19937 g(rd());

    while ( !std::is_sorted(first, last, comp) ) {
        std::shuffle(first, last, g);
    }
}

了解如何see here实施is_sorted

答案 1 :(得分:0)

在评论部分,用户指出了正确的答案,然后回首我只是在扩展它

基本上,比较器是类型而不是对象,因此传递类型对模板类更有意义,因为其元编程(即编译器正在编写自己的代码),因此使类自行创建内部对象更有意义而不是依靠用户来提供。