【问题标题】:Partial template specialization based on lval/rval?基于 lval/rval 的部分模板特化?
【发布时间】:2021-04-03 19:11:21
【问题描述】:

我正在编写一个适配器类来允许我这样写:

for (auto item : ButFirst(myvec)) { ... }

当参数为 lval 时,以下类定义效果很好:

template <typename Container>
class ButFirst {
  const Container& container_;
public:
 ButFirst(const Container& container) : container_(container) {}
  typename Container::const_iterator begin() { return ++(container_.begin()); }
  typename Container::const_iterator end() { return container_.end(); }
};

但是,当参数是 rval 时,我也想使用它,如:

for (auto item : ButFirst(get_vec(3)) { ... }

因此,我将编写以下类定义:

template <typename Container>
class ButFirst {
   const Container container_;

public:
  ButFirst(Container&& container) : container_(std::move(container)) {}
...
};

如何编写一个同时处理两者的类定义?或者可能是一个类和一些部分模板专业化?

【问题讨论】:

  • ButFirst(const Container&amp; container) 应该适用于两者,因为我们可以将右值引用保存到 const 引用中。
  • @martin 引用生命周期延长不够长。

标签: c++ templates overloading rvalue-reference


【解决方案1】:
template <typename Container>
class ButFirst {
  Container container_;
public:
  ButFirst(Container container) : container_(std::forward<Container>(container)) {}
  typename std::decay_t<Container>::const_iterator begin() { return std::next(container_.begin()); }
  typename std::decay_t<Container>::const_iterator end() { return container_.end(); }
};

然后添加扣除指南:

template <typename Container>
ButFirst(Container&&)->ButFirst<Container>;

还有……噗。

尽管看起来这仅在右值上复制。

ButFirst 推导指南中的转发引用在您使用左值构造 Container 时将其推导出为引用类型,在您传递右值时将 Container 推导出为值类型。这正是你想要的。

这不是巧合;转发引用以这种方式工作是有原因的。

live example.

顺便说一句,我的版本有点不同。

我定义了一个range_view 类型,它可以从一个范围类构造,并存储 2 个迭代器。

range_viewbegin() 这样的方法是constrange_view 就像一个指针,而不是一个值。

它有方法:

range_view except_front(std::size_t n=1)const;
range_view except_back(std::size_t n=1)const;
range_view only_front(std::size_t n=1)const;
range_view only_back(std::size_t n=1)const;

根据范围的大小确定n

然后:

for(auto item: range_view(container).except_front())

做你做的。

我考虑检查迭代器计数错误并在这种情况下返回空范围非常值得我从中获得可靠性的开销,因为该数学每次循环发生一次,而不是每次迭代一次。

另外,range_view 还有许多其他有用的用途。

【讨论】:

  • 太棒了。我会认为因为 container_ 不是引用类型,所以它不能引用与左值所引用的对象相同的对象。
  • 如果左值没有被复制,那么我应该能够改变原始左值并看到对 ButFirst 对象的影响,是吗?矢量 myvec{1,2}; ButFirst> bf(myvec); myvec.push_back(3);但是当我使用 bf 进行迭代时,我没有看到 3。这似乎表明左值被复制了。
  • @pnklein godbolt.org/z/a5oKPf7bv -- 你知道for(:) 循环在开始迭代时会创建开始/结束迭代器,对吧?因此,一旦开始迭代,编辑底层容器可能会导致可怕的坏事,包括缺少推回、悬空指针等。container_ 是引用的原因是转发引用 T&amp;&amp; 在传递左值时推导出 T作为引用类型。
  • 是的,我在开始迭代之前尝试过。这真太了不起了。我从你的回复中学到了很多。你是否推荐任何关于这个主题的阅读(除了标准和导致它的建议)?你能分享你的 range_view 代码吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-06
相关资源
最近更新 更多