C ++ 11的std ::设置拉姆达比较功能(C++11 std::set lambda comp

2019-08-16 23:34发布

我想创建一个std::set了自定义的比较函数。 我可以把它定义为一类operator()但我想享受来定义它使用拉姆达的能力,所以我决定在类的构造函数初始化列表,它有确定的lambda函数std::set为成员。 但我不能得到拉姆达的类型。 我开始之前,这里是一个例子:

class Foo
{
private:
     std::set<int, /*???*/> numbers;
public:
     Foo () : numbers ([](int x, int y)
                       {
                           return x < y;
                       })
     {
     }
};

我发现搜索后两种解决方案:一,使用std::function 。 只要有一组比较函数类型std::function<bool (int, int)>并通过酷似我没有拉姆达。 第二种解决方案是写一个MAKE_SET功能,如std::make_pair

解决方案1:

class Foo
{
private:
     std::set<int, std::function<bool (int, int)> numbers;
public:
     Foo () : numbers ([](int x, int y)
                       {
                           return x < y;
                       })
     {
     }
};

方案2:

template <class Key, class Compare>
std::set<Key, Compare> make_set (Compare compare)
{
     return std::set<Key, Compare> (compare);
}

现在的问题是,我有一个很好的理由更喜欢另一种解决方案? 我喜欢第一个,因为它使用的标准功能(MAKE_SET是不是一个标准的功能),但我不知道:不使用std::function使代码(潜在的)慢? 我的意思是,它降低了编译器内联的比较功能的机会,或者它应该是足够聪明的行为完全一样像它会是一个lambda函数类型,不是std::function (我知道,在这种情况下,可以“T是lambda型,但你知道,我问一般的)?

(我用GCC,但我想知道是什么流行的编译器一般做)

总之,在我得到了很大的答案的数量:

如果速度是至关重要的,最好的解决办法是使用具有一流的operator()又名仿函数。 这是最简单的编译器优化和避免任何间接引用。

便于维修和更好的通用解决方案,使用C ++ 11点的特征,使用std::function 。 它仍然(比仿函数慢只是一点点,但它可能是微不足道的)快,你可以使用任何功能- std::function ,λ,任何调用对象。

还有使用函数指针的选择,但如果没有速度的问题,我想std::function更好(如果你使用C ++ 11)。

有定义lambda函数在其他地方的一个选项,但随后你获得从比较功能没有什么是一个lambda表达式,因为你可能也使其具有一流的operator()和定义的位置不会是集建筑无论如何。

还有更多的想法,如使用授权。 如果你希望所有的解决方案的更透彻的解释,阅读的答案:)

Answer 1:

是的,一个std::function介绍几乎不可避免的间接到你set 。 虽然编译器可以随时在理论上,找出所有使用您的setstd::function需要调用它的λ,始终是完全相同的拉姆达,既硬又极为脆弱。

脆弱的,因为之前的编译器可以证明自己是对所有来电std::function实际上是调用您的拉姆达,它必须证明您没有访问权限std::set曾经设定std::function到任何东西,但你的拉姆达。 这意味着它具有追踪所有可能的途径来达到你std::set在所有编译单元,并证明他们没有做到这一点。

这可能在某些情况下是可能的,但相对无害的变化可以打破它,即使你的编译器成功地证明这一点。

在另一方面,一个无状态的仿函数operator()具有容易证明的行为,以及涉及优化,是日常琐事。

所以,是的,在实践中我怀疑std::function可能会比较慢。 在另一方面, std::function的解决方案更易于维护比make_set之一,交换编程时间程序的性能是相当互换。

make_set有严重的缺点,任何此类set的类型必须从通话被推断为make_set 。 通常一set ,你在栈上创建存储持久状态,而不是然后让掉出的范围。

如果你创建了一个静态或全局无国籍拉姆达auto MyComp = [](A const&, A const&)->bool { ... }则可以使用std::set<A, decltype(MyComp)>语法来创建set能坚持,但容易使编译器优化(因为所有实例decltype(MyComp)是无状态的函子)和内联。 我指出这一点,因为你坚持了setstruct 。 (或者,你的编译器支持

struct Foo {
  auto mySet = make_set<int>([](int l, int r){ return l<r; });
};

我会觉得奇怪!)

最后,如果你担心性能,考虑std::unordered_set快得多(在暂时无法遍历为了内容,不必编写成本/找到一个好的哈希),而排序的std::vector是更好,如果你有一个2相“多次查询内容”“插入一切”即可。 只需将它塞进了vector ,然后再sort unique erase ,然后使用免费equal_range算法。



Answer 2:

这是不可能的编译器将能够内联的std ::函数调用,而支持lambda表达式几乎肯定会内联的函子版本,其中包括如果该函子是不是由隐藏在拉姆达任何编译器std::function

你可以使用decltype获得拉姆达的比较类型:

#include <set>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
   auto comp = [](int x, int y){ return x < y; };
   auto set  = std::set<int,decltype(comp)>( comp );

   set.insert(1);
   set.insert(10);
   set.insert(1); // Dupe!
   set.insert(2);

   std::copy( set.begin(), set.end(), std::ostream_iterator<int>(std::cout, "\n") );
}

它打印:

1
2
10

看到它在现场运行Coliru



Answer 3:

一个无状态的λ(即一个不带摄像)可以衰减到一个函数指针,所以你的类型可以是:

std::set<int, bool (*)(int, int)> numbers;

否则,我会去的make_set解决方案。 如果因为它是非标准的,你不会得到太多的代码编写的,你不会使用一个在线生成功能!



Answer 4:

从我的经验与探查玩耍,性能与美之间的最佳平衡是使用自定义的委托实现,如:

https://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11

由于std::function通常是有点过重。 我不能在你的具体情况发表评论,因为我不认识他们,但。



Answer 5:

如果你决定有set作为类的成员,它的初始化在构造时间比较,然后间接至少一个级别是不可避免的。 想想看,只要编译器知道,你可以添加其他的构造函数:

 Foo () : numbers ([](int x, int y)
                   {
                       return x < y;
                   })
 {
 }

 Foo (char) : numbers ([](int x, int y)
                   {
                       return x > y;
                   })
 {
 }

一旦你有类型的对象Foo的类型set不进行其构造初始化它的比较信息,所以要调用正确的拉姆达需要一个间接的方式运行时选择的拉姆达operator()

由于您使用的captureless lambda表达式,你可以使用函数指针类型bool (*)(int, int)作为比较类型,captureless lambda表达式有相应的转换功能。 这当然会涉及通过函数指针的间接。



Answer 6:

所不同的高度取决于你的编译器的优化。 如果它在一个优化的λ std::function这些都是等价的,如果不是你介绍在前者的间接,你不会在后者有。



文章来源: C++11 std::set lambda comparison function