基于 lval/rval 的部分模板专业化?

Partial template specialization based on lval/rval?

我正在编写一个适配器 class 以允许我这样写:

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

以下 class 定义在参数为 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)) { ... }

因此,我会写出以下 class 定义:

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

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

如何编写一个 class 定义来处理这两种情况?或者可能是一个 class 和一些部分模板专业化?

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 还有许多其他有用的用途。