使用常量迭代器对唯一指针的向量进行排序

Sorting a vector of unique pointers using constant iterators

我的问题是为什么我可以使用 begin/end 而不是 cbegin/cend

对唯一指针向量进行排序

这样编译:

std::sort(teachers.begin(), teachers.end(), compare_by_uniqptr);

但出于某种原因,这让我尝试引用已删除的函数的 C2280。

std::sort(teachers.cbegin(), teachers.cend(), compare_by_uniqptr);

这是一些背景信息。 'teachers' 是 Teacher 对象的唯一指针向量。

class Teacher : public Person {
private:
  std::vector<std::unique_ptr<Student>> students;
public:
  Teacher(std::string name) : Person(name) {}
  void insert(std::string name);
  int num_students() { return students.size(); }
};

std::vector<std::unique_ptr<Teacher>> teachers;

bool compare_by_uniqptr(const std::unique_ptr<Teacher>& a,
                        const std::unique_ptr<Teacher>& b) 
{
  return a.get()->num_students() > b.get()->num_students();
}

我按每个人的学生人数降序排列。 begin/end 编译但 cbegin/cend 不编译。为什么?

因为 std::sort 通过 移动 对范围内的元素进行排序。这意味着将通过迭代器修改元素;它不适用于 const.

的迭代器

不能复制唯一指针(否则会失去唯一性)。因此,当它们被排序时,它们将需要被移动(或交换)。这些操作需要更改唯一指针的 internals。当然,您不能通过 const 引用更改对象的内部结构。

为什么 cbegin()cend() 不起作用? documentation 中所述的 std::sort() 具有以下类型要求:

-RandomIt must meet the requirements of ValueSwappable and RandomAccessIterator.

-The type of dereferenced RandomIt must meet the requirements of MoveAssignable and MoveConstructible.

-Compare must meet the requirements of Compare.

重点是我的。所以 std::vector::const_iterator 不满足这些要求。因此,即使您有 std::vector<int> 并尝试使用它们也不起作用。如果你问为什么它在 begin()/end()std::unique_ptr 上工作,那么你也可以在 documentation 中看到:

std::swap(std::unique_ptr) (C++11) specializes the std::swap algorithm (function template)

所以std::unique_ptr因为值满足ValueSwappable概念。

这是一个解决方案。向下兼容 c++11:

#include <vector>
#include <string>
#include <memory>
#include <algorithm>

struct Person {
    Person(std::string name);
};

struct Student : Person {

};

class Teacher : public Person {
private:
  std::vector<std::unique_ptr<Student>> students;
public:
  Teacher(std::string name) : Person(name) {}
  void insert(std::string name);
  int num_students() const { return students.size(); }
};

std::vector<std::unique_ptr<Teacher>> teachers;

struct by_num_students
{
    bool operator()(Teacher const& l, Teacher const& r) const
    {
        return l.num_students() < r.num_students();
    }
};

template<class Comp>
struct by_pointee_impl
{
    template<class L, class R>
    bool operator()(L&& l, R&& r) const 
    {
        return comp(*l, *r);
    }

    Comp comp;
};
template<class Comp>
auto by_pointee(Comp comp) -> by_pointee_impl<Comp>
{
    return by_pointee_impl<Comp>{comp};
};

int main()
{
    std::sort(begin(teachers), end(teachers), by_pointee(by_num_students()));
}