C++ std::greater<&燃气轮机;wо;在一组中?

C++ std::greater<&燃气轮机;wо;在一组中?,c++,c++14,C++,C++14,我知道std::greater是如何工作的。但是当我阅读C++14之后的std::greater API时,它有一个默认类型void。所以,如果我们不将任何模板参数传递给更大的值,它将默认为void,如下所示。但结果与预期一样,按降序排列 #include <iostream> #include <set> template< class T = void > struct greater; int main() { std::set<int

我知道std::greater是如何工作的。但是当我阅读C++14之后的std::greater API时,它有一个默认类型void。所以,如果我们不将任何模板参数传递给更大的值,它将默认为void,如下所示。但结果与预期一样,按降序排列

#include <iostream>
#include <set>

template< class T = void >
struct greater;

int main()
{
    std::set<int, std::greater<>> s {4, 5, 6, 7}; // This transforms to std::set<int, std::greater<void>>
}
#包括
#包括
模板
结构更大;
int main()
{
std::set s{4,5,6,7};//这将转换为std::set
}

有人能解释一下这种专门化是如何工作的吗?

它的工作原理是将调用操作符作为函数模板而不是函数。将旧版本与新专业进行比较:

// greater<T>
bool operator()( const T& lhs, const T& rhs ) const;

// greater<void> i.e. greater<>
template< class T, class U>
auto operator()( T&& lhs, U&& rhs ) const;
//更大
布尔运算符()(常数T和lhs、常数T和rhs)常数;
//更大,即更大
模板
自动运算符()(T&&lhs,U&&rhs)常量;

这一功能的强大之处在于能够比较不同类型的对象

例如,在给定字符串视图的情况下,这是至关重要的,您需要查找是否在集合中存储了等效的
std::string
。能够将字符串视图与
std::string
进行比较允许我们不从该字符串视图创建字符串。这很好,因为创建
std::string
可能会很昂贵


使用旧的比较器,它只能将
std::string
与另一个
std::string
进行比较,在这种情况下,我们必须创建一个
std::string
,以便从一组
std::string

中查找它,这是一种非常简单的技术,但我仍然对它着迷,因为它非常强大。基本思想是将特定类型的实例化推迟到模板化的
操作符()的调用,而不是对特定类型的实例化
更大的

假设您希望将重载集传递给函数,以便执行以下操作:

template <typename F>
void foo(F f){
    f(std::string{});  
    f(42);             
}
在您的情况下,传递
greater
而不是
greater
主要是为了方便,但想法是一样的
greater
具有模板化的
运算符()


附:…我错过了
greater::operator()
的一个关键优势(详细解释见)
greater
允许您比较不同类型的元素,即使它们之间没有隐式转换,或者您不想触发转换(当然您仍然需要一个可行的
)。

实例化模板化函数也比实例化新类型更快,AFAICR.@einpoklum在需要实例化的
greater
及其
greater
加上需要实例化的
操作符()的情况下是很好的。不过有趣的是,如果你知道一些参考资料,我很乐意阅读。奥丁·霍姆斯的《2017年潜水密码》演讲,@einpoklum《很好的演讲》。糟糕的配色方案,但在其他方面都非常出色。不过,我认为您记错了,根据“Bob”的说法,实例化一个函数的代价更高(~43:20)。
struct bar {
    template <typename T>
    void operator()(T t) {}
};


int main() {
  foo(bar{});  
}