【问题标题】:C++ use std::greater() in priority_queue and sortC++ 在 priority_queue 中使用 std::greater() 并排序
【发布时间】:2019-11-16 05:02:41
【问题描述】:

为什么这两种情况的文档都说了同样的话,但它们以相反的方式声明,一种使用greater<int>,而另一种使用greater<int>()。有人能解释一下吗?

文档priority_queue cpp library 说comp

可以是用于对堆进行排序的比较对象。这可能是一个 函数指针或函数对象

priority_queue<int, vector<int>, greater<int>> minheap;  //works
priority_queue<int, vector<int>, greater<int>() > minheap; //why fail?

cpp library sort 的文档也说了同样的话,即 comp 既可以是函数指针,也可以是函数对象。

sort (numbers.begin(), numbers.end(), std::greater<int>()); //works
sort (numbers.begin(), numbers.end(), std::greater<int>); //fail

【问题讨论】:

  • 一个是模板参数,另一个是函数参数(&lt;&gt;和()不同)。
  • 但是文档说这两种情况都应该能够使用函数参数(函数对象或指针),因此 Greater() 应该适用于这两种情况,对吧?为什么priority_queue会失败
  • 是的,如果你将它传递给构造函数,而不是作为模板参数。您可以在模板参数中指定类型 std::greater&lt;int&gt;,然后在构造函数参数中传递该类型的对象 std::greater&lt;int&gt;()。
  • 我很惊讶没有人提到当用作类型时,std::greater&lt;int&gt;() 不仅仅是一个直接错误,而是一个表示返回 std::greater&lt;int&gt; 的函数的有效类型。当编译器意识到函数类型不适合作为比较器时,您会从编译器得到错误。
  • @eerorika Greater() 是类型说明符或表达式,具体取决于使用的上下文。:) 例如,如果您将构造括在括号中,则不会有歧义。 :)

标签: c++ c++11 object templates types


【解决方案1】:

std::priority_queue 的第三个模板参数是比较器的类型。

使用greater&lt;int&gt;() 创建greater&lt;int&gt; 类的实例(对象),并将此实例(对象)作为参数而不是greater&lt;int&gt; 类型传递。

对于std::sort,第三个函数参数是一个实际的对象(或者更确切地说,任何可调用的对象)。


关于您链接到的引用,它是 构造函数 的引用,而不是 std::priority_queue 模板本身。构造函数参数类似于函数参数,与模板参数有很大不同。

【讨论】:

  • 感谢您的帮助!但是priority_queue的文档说可以使用function object,为什么会失败呢?
  • @coder 您正在查看std::priority_queue constructor 参考,您确实可以在其中传递一个可调用对象,例如std::greater&lt;int&gt;()(或任何其他可调用的)。
  • 你是对的。但是,为什么priority_queue&lt;int, vector&lt;int&gt;&gt; pq(greater&lt;int&gt;()) pq; 声明会失败?能否请您展示如何使用greater&lt;int&gt;() 构造一个priority_queue?
  • @coder 这个定义是完全错误的,因为你可以做类似int a{0} a;的事情你的意思是例如priority_queue&lt;int, vector&lt;int&gt;&gt; pq{greater&lt;int&gt;()};?
【解决方案2】:

在此声明中

priority_queue<int, vector<int>, greater<int>> minheap; 

greater&lt;int&gt;是一个模板参数,它指定一个类型,对应priority_queue的类型模板参数class Compare

template<class T, class Container = vector<T>,
class Compare = less<typename Container::value_type>>
class priority_queue;

在此声明中

sort (numbers.begin(), numbers.end(), std::greater<int>());

使用默认构造函数创建greater&lt;int&gt;类型的临时对象。

std::greater是一个模板结构

template<class T = void> struct greater;

那是它的特化提供了一个类型。

您可以在声明中使用模板特化作为类型说明符。例如

std::greater<int> obj1;
std::greater<double> obj2;
std::greater<std::string> obj3;

在第一种情况下,您必须为相应的类型模板参数指定类型参数。

在第二种情况下,您必须指定 greater&lt;int&gt; 类型的对象。

请注意,如果您尝试使用记录 std::greater&lt;int&gt;() 作为 priority_queue 的模板参数,那么编译器会将其视为具有返回类型 std::greater&lt;int&gt; 且没有参数的函数类型说明符。因为编译器期望类型说明符作为模板参数而不是表达式。但是将这样的函数类型指定为类型模板参数对于 priority_queue 没有意义。

为了更清楚,重写 std::sort 的调用如下

std::greater<int> obj;
sort (numbers.begin(), numbers.end(), obj );

正如您在此处看到的,std::greater&lt;int&gt; 是变量 obj 的类型说明符,它作为参数提供给函数 std::sort。上面的调用和这个调用的区别

sort (numbers.begin(), numbers.end(), std::greater<int>());

是在最后一种情况下创建了一个std::greater&lt;int&gt; 类型的临时对象,例如std::greater&lt;int&gt;()。

这是一个演示程序

#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>

int main() 
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';

    std::greater<int> comp;

    //  Here is used an already created object of the type std::greater<int> 
    std::sort( std::begin( a ), std::end( a ), comp );

    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';

    //  Here is used a remporary object of the type std::less<int> 
    std::sort( std::begin( a ), std::end( a ), std::less<int>() );

    for ( const auto &item : a ) std::cout << item << ' ';
    std::cout << '\n';

    return 0;
}

程序输出是

0 1 2 3 4 5 6 7 8 9 
9 8 7 6 5 4 3 2 1 0 
0 1 2 3 4 5 6 7 8 9 

【讨论】:

  • 谢谢您,先生!那么,sort() 使用了函数对象,但是为什么模板参数对 sort() 不起作用?
  • @coder std:;sort 需要一个函数对象而不是类型说明符。那就是您需要提供一个表达式作为参数。 std::sort 已经将参数声明为具有某种类型。
【解决方案3】:

简而言之,当您需要为模板参数提供类型时,如果您为该类型提供默认构造函数的调用,例如,默认构造实例,则会出现编译时错误。

同样,当你需要提供实例时,比如排序函数的第三个参数,如果你提供类型你会得到编译时错误,如果你提供默认构造实例,就可以了。

【讨论】:

    【解决方案4】:

    仅取决于您是否需要类型(定义类型时)或值(调用模板函数时),您分别需要类型或默认构造的类型实例。

    请注意,您可以有使用 greater&lt;int&gt;{} greater&lt;int&gt; 的相反等价物。对于sort,编译器推导类型,您可以省略类型推导。在priority_queue 的情况下,您可以使用 C++17 并使编译器根据构造函数参数推断类型。所以这里的代码看起来很奇怪,但它可以编译:

    priority_queue a{ greater<int>{}, vector<int>{} };  
    
    int tab[] { 3, 4, 5, 0 };
    sort<int*, greater<int>>(tab, tab + 3, {});
    

    https://wandbox.org/permlink/X4aHIzvOqAlOWGoP

    【讨论】:

      猜你喜欢
      • 2018-09-18
      • 1970-01-01
      • 2015-12-21
      • 2013-02-01
      • 1970-01-01
      • 2019-02-11
      • 2013-08-05
      • 2023-02-09
      • 1970-01-01
      相关资源
      最近更新 更多