【问题标题】:Dereference wrapper for vector of unique_ptr?unique_ptr 向量的取消引用包装?
【发布时间】:2018-01-10 16:18:40
【问题描述】:

我最近遇到了这个article;它提供了 boost 的 indirect_iterator 的基本实现,但适用于 unique_ptr 迭代器。我决定稍微调整一下文章中的示例,以便可以将其用于类成员之外的向量:

template <typename T>
using SPN = std::unique_ptr<Node<T>>;

template <class BaseIterator>
struct DReferenceIterator : BaseIterator
{
  explicit DReferenceIterator(const BaseIterator & other) : BaseIterator(other) {}
  auto & operator*() const { return *(this->BaseIterator::operator*()); }
  auto * operator->() const { return this->BaseIterator::operator*().get(); }
  auto & operator[](size_t n) const { return *(this->BaseIterator::operator[](n)); }
};

template<typename T>
auto begin_t(std::vector<SPN<T>> & v) 
{
  return DReferenceIterator<typename std::vector<SPN<T>>::iterator>(v.begin());
}

template<typename T>
auto end_t(std::vector<SPN<T>> & v) 
{
  return DReferenceIterator<typename std::vector<SPN<T>>::iterator>(v.end());
}

我可以毫无问题地使用std::find(begin_t(v), end_t(v), value)。但是,如果我尝试拨打std::sort(begin_t(v), end_t(v)),它根本不起作用。我确实在Node&lt;T&gt; 类中提供了operator&lt;() 的实现,但由于某种原因,我的代码无法编译。我得到的编译错误非常广泛且难以阅读,但是,我设法提取了一些我认为导致问题的东西:

二进制=:未找到采用std::unique_ptr&lt;Node&lt;float&gt;,std::default_delete&lt;_Ty&gt;&gt; 类型右侧操作数的运算符(或没有可接受的转换)

此消息是否意味着std::sort() 正在尝试复制unique_ptr?在这种情况下,这是否意味着 DReferenceIterator 包装器无法按预期工作?我对 C++ 还很陌生,所以如果不是这种情况,请帮助我了解问题到底出在哪里。

【问题讨论】:

    标签: c++11 vector iterator wrapper unique-ptr


    【解决方案1】:

    您的DReferenceIterator 向标准库谎报它所公开的内容。这会导致您在尝试 std::sort 时看到的错误。

    通过从 BaseIterator 派生,您将继承以下(以及其他)BaseIterator::value_typeBaseIterator::pointerBaseIterator::reference,它们与您的 operators 的返回类型不匹配。

    正确的实现应该更像

    template <class BaseIterator>
    struct DReferenceIterator
    {
      using value_type = BaseIterator::value_type::element_type;
      using reference = value_type &;
      using pointer = value_type *;
      using difference_type = BaseIterator::difference_type
      using iterator_category = BaseIterator::iterator_category;
    
      explicit DReferenceIterator(const BaseIterator & other) : other(other) {}
    
      reference operator*() const { return **other; }
      pointer   operator->() const { return (*other).get(); }
      reference operator[](size_t n) const { return (*other)[n]; }
    
      DReferenceIterator& operator++() { ++other; return *this; }
      DReferenceIterator& operator--() { --other; return *this; }
    
      DReferenceIterator& operator+=(difference_type n) { other += n; return *this; }
      DReferenceIterator& operator-=(difference_type n) { other -= n; return *this; }
      difference_type operator-(DReferenceIterator& rhs) { return other - rhs.other; }
      bool operator<(DReferenceIterator& rhs) { return other < rhs.other; }
    
      // And all the other operators, in terms of those      
    
    private: 
      BaseIterator other;
    };
    

    【讨论】:

    • 对迭代器没有 SFINAE 友好的要求,所以你可以只添加 all 的操作并依靠 std 库不调用它们,并且不被调用它们就赢了'不被实例化。
    • @Yakk:介意解释如何在涉及迭代器的操作上不使用 SFINAE 来满足std::distance 的要求吗? “由于只有随机访问迭代器提供 +- 运算符,因此库提供了两个函数模板 advancedistance。这些函数模板使用 +- 用于随机访问迭代器(因此, 它们的时间恒定);对于输入、前向和双向迭代器,它们使用++ 来提供线性时间实现。”
    • 你能解释一下,为什么std::find 似乎有效吗?
    • @BenVoigt 您查询std::iterator_traits&lt;iterator&gt;::iterator_category ,如果+- 已定义,则根据它而不是根据它进行调度。 void print_tag_name( std::random_access_iterator_tag ) { std::cout &lt;&lt; "random\n"; } 等(每个迭代器类别标记一个 overload),然后 template&lt;class T&gt; void print_iterator_type() { print_tag_name( typename std::iterator_traits&lt;T&gt;::iterator_category{} ); } 没有 SFINAE 并且适用于任何(兼容的)迭代器。使用它在++ 循环和+ 之间进行选择留作练习。
    • @Oli_ver18 该标准对格式错误的程序没有任何限制:在没有适当且准确的迭代器类别的情况下调用 std 算法是格式错误的,但有时它仍然有效。实际上,您的标准库对 std::find 的特定实现不需要这些 typedef 是正确的,所以它起作用了。排序实现以一种破坏的方式使用这些类型定义。两者都不需要工作,也不需要(按照标准)编译失败,甚至做任何合理的事情。
    猜你喜欢
    • 2018-05-31
    • 2019-10-29
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 2023-03-23
    相关资源
    最近更新 更多