基于 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
为值类型一个右值。这正是您想要的。
这不是巧合;转发引用以这种方式工作是有原因的。
顺便说一句,我的版本有点不同。
我定义了一个 range_view
类型,它可以从类范围构造,并存储 2 个迭代器。
range_view
像 begin()
这样的方法是 const
; range_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
还有许多其他有用的用途。
我正在编写一个适配器 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
为值类型一个右值。这正是您想要的。
这不是巧合;转发引用以这种方式工作是有原因的。
顺便说一句,我的版本有点不同。
我定义了一个 range_view
类型,它可以从类范围构造,并存储 2 个迭代器。
range_view
像 begin()
这样的方法是 const
; range_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
还有许多其他有用的用途。