C++11 std::函数和std::reference包装器,用于对std::set进行排序

C++11 std::function and std::reference wrapper used for sorting std::set

本文关键字:std set 排序 用于 函数 reference 包装 C++11      更新时间:2023-10-16

我有一个C++类,它的一个字段是一组std::对象。我想编写自己的比较函数,或者让用户指定一个。在C++11中,有一种处理泛型函数类型的新方法:std::function。它适用于函数指针、成员函数指针、lambda函数等。

我试着写一个简单的实验程序,但它一直在崩溃,即使我做C++11维基百科文章建议的事情。也许我只是不明白std::function和std::ref应该如何使用。

总之,关键是当我从一个简单的lambda函数创建一个std::函数并将其作为类成员时,类的sizeof增加了22。当我从一个指向全局函数的指针创建一个std::函数时,这个std::函数的sizeof是32。所以尺寸很大。我将有许多对象使用相同的比较函数,所以我更喜欢所有对象都使用一个函数。

我有两个想法,告诉我你的想法。一个想法是,使用std::ref来存储对函数的引用,这样我就可以定义一个函数,许多对象将使用它来比较std::set元素。第二个想法:如果它不能这样工作,或者生成的函数对象太大,也许我可以使用shared_ptr。

您可能会想:为什么不拥有一个静态std::函数成员?答案是:因为所有对象都会使用相同的比较函数。例如,我希望能够拥有1000个对象,其中400个使用一个比较函数,600个使用不同的比较函数。

示例:

class MyClass
{
public:
private:
     std::function<bool (int, int)> compare;
     std::set<int> set;
};

现在,我如何让std::集使用std:函数,并让许多MyClass对象使用相同的函数?

我希望能够在运行时更改比较函数,这样用户就可以选择集合中对象的顺序(由GUI显示)。

表示共享所有权的标准方法是使用std::shared_ptr。这增加了更多的开销,迫使您在堆上分配std::function,但shared_ptrstd::function小,它将正确管理其生存期,因此当任何对象仍在使用函数对象时,它将保持活动状态,并在不再需要时自动销毁。

正如您所建议的,引用共享函数的reference_wrapper可以用作set的比较对象,因为如果reference_wrapper包装了可调用类型,则它是可调用的。

class MyClass
{
     typedef std::function<bool (int, int)> func_type;
public:
     MyClass(std::shared_ptr<func_type> const& f)
     : compare(f), set( std::ref(*f) )
     { }
private:
     std::shared_ptr<func_type> compare;
     std::set<int, std::reference_wrapper<func_type>> set;
};

reference_wrapper不能为null(像引用一样),因此必须使用有效的reference_wrapper对象构造std::set

由于std::set中的std::reference_wrapper只包含一个指向std::function的非拥有指针,因此需要小心在更新shared_ptr的同时更新集合的比较对象,或者可以删除对该函数的最后一个引用,因此shared_ptr会破坏它,在集合中留下一个悬空指针。可以这样做:

void MyClass::replace_cmp(std::shared_ptr<func_type> const& f)
{
  set = std::set<int, std::reference_wrapper<func_type>>( std::ref(*f) );
  compare = f;
}

您告诉集合在构造函数初始值设定项列表中使用比较函数:

class MyClass
{
public:
    template<typename Fc>
    MyClass(Fc compare_func)
        : compare(compare_func),  // Initialize the comparison function
          set(compare)            // Tell the set to use out function for comparison
        {}
};