在C++数组和SequenceContainers http://en.cppreference.com/w/cpp/concept/SequenceContainer (std::array
、std::deque
、std::vector
)中,必然是零索引的.但是,从语义的角度来看,数组的元素可以被分配给完全其他范围的索引。一个常见的例子是需要在0索引容器中存储一个1索引数组。(不过,我记得必须对任意索引的数组进行操作)
解决上述问题的办法包括:
vec[n-1]
之类的代码--但是这种方法繁琐且容易出错(很容易忘记这个-1
,而是键入vec[n]
,oops);some_type &operator [] (unsigned n) {return m_arr[n-1];}
的方法--但这通常是非常过分的;我想我应该为SequenceContainers编写一个包装器,它与标准容器的不同之处在于,它的operator[]
和at
函数将使它们的参数偏离给定的偏移量。我编写了这个包装器的两个版本:一个是确定编译时作为模板参数,另一个是可以修改运行时。我认为两者都是必要的。
用法很简单,如计算Collatz序列前100个初始值的长度的一个简单示例所示:( https://en.wikipedia.org/wiki/Collatz_猜想 )(对不起,朴素算法)
#include "shiftsc.hpp"
using namespace std;
using element = unsigned;
using length = unsigned;
length compute_length(element n)
{return n == 1 ? 1 : (1 + compute_length(n % 2 == 0 ? n / 2 : n * 3 + 1));}
int main()
{
constexpr element max_element = 100;
// one-indexed std::array<length, max_element>
shifted_SequenceContainer<array<length, max_element>, 1> Collatz_lengths;
// or for short: shifted_array<length, max_element, 1> Collatz_lengths;
for(element i = 1; i <= max_element; i++)
Collatz_lengths[i] = compute_length(i);
}
见工作:http://ideone.com/Xy8A2U
(稍后,我将在一个单独的问题中,为运行时偏移的变化提供一个用例-我对另一个需要使用此功能的包装器有一个想法。)
在向您介绍代码之前,我必须先写下一些我可以看到的问题--我不知道如何修复…(inb4近距离投票:他们不会让代码被破坏!)
virtual
与模板不兼容的b/c是不可能的(不能允许重载方法,比如template <class Iterator> void assign (Iterator it, Iterator jt)
;·用宏定义重复方法和整个类;这确实有效,确实缩短了代码,但IMO使它完全不可读,所以我放弃了。)std::array
可以用大括号内的列表初始化;不幸的是,shifted_SequenceContainer<std::array</*...*/>/*...*/>
不能.我对如何解决这个问题没有太多的想法。https://stackoverflow.com/questions/40599052/how-to-write-a-wrapper-for-stdarrays-list-initialization-constructor https://stackoverflow.com/questions/40599829/how-to-pass-a-braced-init-list-to-stdarray-s-constructorshifted_SequenceContainer
打破了所有的命名约定…但我能做什么呢?这个概念叫做SequenceContainer
,所以如果我改变了它,我就不再引用这个概念…它被称为SequenceContainer
而不是sequence_container
,因此像shifted_sequence_container
这样的名称不合适,而像ShiftedSequenceContainer
这样的名称与标准容器的命名不一致--毕竟,std::unordered_map
被称为unordered_map
,而不是UnorderedMap
。(冗长的)序言已经够多了,下面是实际代码:
#ifndef GAAZKAM_SHIFTSC_H
#define GAAZKAM_SHIFTSC_H
#include <utility>
#include <initializer_list>
#include <stdexcept>
#include <cstddef>
#include <array>
#include <vector>
#include <deque>
// Known problems: syntax like shifted_array<int, 4, 7> arr({1,2,3,4}) wont work
// With static offset
// Container must satisfy SequenceContainer
template<class Container, typename Container::difference_type Offset>
class shifted_SequenceContainer
{
public:
using value_type = typename Container::value_type;
using reference = typename Container::reference;
using const_reference = typename Container::const_reference;
using pointer = typename Container::pointer;
using const_pointer = typename Container::const_pointer;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using reverse_iterator = typename Container::reverse_iterator;
using const_reverse_iterator = typename Container::const_reverse_iterator;
using difference_type = typename Container::difference_type;
using size_type = typename Container::size_type;
using container_type = Container;
// Must be public; otherwise the user mayn’t access e.g. vector::reserve,
// which might be needed for optimization
Container cont;
static constexpr typename Container::difference_type offset = Offset;
explicit shifted_SequenceContainer() {}
shifted_SequenceContainer(shifted_SequenceContainer const &that)
: cont(that.cont) {}
shifted_SequenceContainer(shifted_SequenceContainer &&that)
: cont(std::move(that.cont)) {}
explicit shifted_SequenceContainer(Container const &that) : cont(that) {}
explicit shifted_SequenceContainer(Container &&that) : cont(that) {}
shifted_SequenceContainer &operator = (shifted_SequenceContainer const &that)
{cont = that.cont; return *this;}
shifted_SequenceContainer &operator = (shifted_SequenceContainer &&that)
{cont = std::move(that.cont); return *this;}
shifted_SequenceContainer &operator = (Container const &that)
{cont = that; return *this;}
shifted_SequenceContainer &operator = (Container &&that)
{cont = std::move(that); return *this;}
shifted_SequenceContainer
(size_type n, value_type const &val = value_type())
: cont(n, val) {}
template<class Iterator> shifted_SequenceContainer(Iterator it, Iterator jt)
: cont(it, jt) {}
shifted_SequenceContainer(std::initializer_list<value_type> init)
: cont(init) {}
shifted_SequenceContainer &operator = (std::initializer_list<value_type> init)
{cont = init; return *this;}
void assign(size_type n, value_type const &val) {return cont.assign(n, val);}
template<class Iterator> void assign(Iterator it, Iterator jt)
{return cont.assign(it, jt);}
void assign(std::initializer_list<value_type> init)
{return cont.assign(init);}
void swap(shifted_SequenceContainer &that) {return cont.swap(that.cont);}
friend void swap
(shifted_SequenceContainer &lhs, shifted_SequenceContainer &rhs)
{return swap(lhs.cont, rhs.cont);}
friend bool operator ==
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont == rhs.cont;}
friend bool operator !=
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont != rhs.cont;}
friend bool operator <
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont < rhs.cont;}
friend bool operator <=
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont <= rhs.cont;}
friend bool operator >
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont > rhs.cont;}
friend bool operator >=
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont >= rhs.cont;}
constexpr size_type size() const {return cont.size();}
constexpr size_type max_size() const {return cont.max_size();}
void resize(size_type n) {return cont.resize(n);}
void resize(size_type n, value_type const &val) {return cont.resize(n, val);}
constexpr bool empty() const {return cont.empty();}
template<class ...Args> iterator emplace(const_iterator pos, Args &&...args)
{return cont.emplace(pos, std::move(args...));}
template<class ...Args> void emplace_front(Args &&...args)
{return cont.emplace_front(std::move(args...));}
template<class ...Args> void emplace_back(Args &&...args)
{return cont.emplace_back(std::move(args...));}
iterator insert(const_iterator pos, value_type const &val)
{return cont.insert(pos, val);}
iterator insert(const_iterator pos, value_type &&val)
{return cont.insert(pos, std::move(val));}
iterator insert(const_iterator pos, size_type n, value_type const &val)
{return cont.insert(pos, n, val);}
template<class Iterator> void insert(iterator pos, Iterator it, Iterator jt)
{return cont.insert(pos, it, jt);}
iterator insert(const_iterator pos, std::initializer_list<value_type> init)
{return cont.insert(pos, init);}
void push_front(value_type const &val) {return cont.push_front(val);}
void push_front(value_type &&val) {return cont.push_front(std::move(val));}
void push_back(value_type const &val) {return cont.push_back(val);}
void push_back(value_type &&val) {return cont.push_back(std::move(val));}
void clear() {return cont.clear();}
iterator erase(const_iterator pos) {return cont.erase(pos);}
iterator erase(const_iterator it, const_iterator jt)
{return cont.erase(it, jt);}
void pop_front() {return cont.pop_front();}
void pop_back() {return cont.pop_back();}
iterator begin() {return cont.begin();}
constexpr const_iterator begin() const {return cont.begin();}
constexpr const_iterator cbegin() const {return cont.cbegin();}
iterator end() {return cont.end();}
constexpr const_iterator end() const {return cont.end();}
constexpr const_iterator cend() const {return cont.cend();}
reverse_iterator rbegin() {return cont.rbegin();}
constexpr const_reverse_iterator rbegin() const {return cont.rbegin();}
constexpr const_reverse_iterator crbegin() const {return cont.crbegin();}
reverse_iterator rend() {return cont.rend();}
constexpr const_reverse_iterator rend() const {return cont.rend();}
constexpr const_reverse_iterator crend() const {return cont.crend();}
reference front() {return cont.front();}
constexpr const_reference front() const {return cont.front();}
reference back() {return cont.back();}
constexpr const_reference back() const {return cont.back();}
// Here things actually start to change
reference operator[] (difference_type pos)
{return cont.operator[](pos-offset);}
constexpr const_reference operator[] (difference_type pos) const
{return cont.operator[](pos-offset);}
reference at (difference_type pos)
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
constexpr const_reference at (difference_type pos) const
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
// Additionally let’s provide these ones:
iterator find(difference_type pos)
{return cont.begin()+(pos-offset);}
constexpr const_iterator find(difference_type pos) const
{return cont.cbegin()+(pos-offset);}
};
template
<class T, std::size_t N, typename std::array<T, N>::difference_type offset>
using shifted_array = shifted_SequenceContainer<std::array<T, N>, offset>;
template<class T, typename std::vector<T>::difference_type offset>
using shifted_vector = shifted_SequenceContainer<std::vector<T>, offset>;
template<class T, typename std::deque<T>::difference_type offset>
using shifted_deque = shifted_SequenceContainer<std::deque<T>, offset>;
// With dynamic offset
// Container must satisfy SequenceContainer
template<class Container>
class dynshifted_SequenceContainer
{
public:
using value_type = typename Container::value_type;
using reference = typename Container::reference;
using const_reference = typename Container::const_reference;
using pointer = typename Container::pointer;
using const_pointer = typename Container::const_pointer;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using reverse_iterator = typename Container::reverse_iterator;
using const_reverse_iterator = typename Container::const_reverse_iterator;
using difference_type = typename Container::difference_type;
using size_type = typename Container::size_type;
using container_type = Container;
// Must be public; otherwise the user mayn’t access e.g. vector::reserve,
// which might be needed for optimization
Container cont;
difference_type offset = 0;
explicit dynshifted_SequenceContainer() {}
dynshifted_SequenceContainer(dynshifted_SequenceContainer const &that)
: cont(that.cont), offset(that.offset) {}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer
(shifted_SequenceContainer<Container, StaticOffset> const &that)
: cont(that.cont), offset(StaticOffset) {}
dynshifted_SequenceContainer(dynshifted_SequenceContainer &&that)
: cont(std::move(that.cont)), offset(that.offset) {}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer
(shifted_SequenceContainer<Container, StaticOffset> &&that)
: cont(std::move(that.cont)), offset(StaticOffset) {}
explicit dynshifted_SequenceContainer
(Container const &that, difference_type off = difference_type{})
: cont(that), offset(off) {}
explicit dynshifted_SequenceContainer
(Container &&that, difference_type off = difference_type{})
: cont(that), offset(off) {}
dynshifted_SequenceContainer &operator =
(dynshifted_SequenceContainer const &that)
{offset = that.offset; cont = that.cont; return *this;}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer &operator =
(shifted_SequenceContainer<Container, StaticOffset> const &that)
{cont = that.cont; offset = StaticOffset; return *this;}
dynshifted_SequenceContainer &operator = (dynshifted_SequenceContainer &&that)
{offset = that.offset; cont = std::move(that.cont); return *this;}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer &operator =
(shifted_SequenceContainer<Container, StaticOffset> &&that)
{{cont = std::move(that.cont); offset = StaticOffset; return *this;}}
dynshifted_SequenceContainer &operator = (Container const &that)
{cont = that; return *this;}
dynshifted_SequenceContainer &operator = (Container &&that)
{cont = std::move(that); return *this;}
explicit dynshifted_SequenceContainer
(
size_type n,
value_type const &val = value_type(),
difference_type offset = 0
)
: cont(n, val), offset(offset) {}
explicit dynshifted_SequenceContainer(size_type n, difference_type offset)
: cont(n, value_type{}), offset(offset) {}
template<class Iterator> dynshifted_SequenceContainer
(Iterator it, Iterator jt, difference_type offset = 0)
: cont(it, jt), offset(offset) {}
dynshifted_SequenceContainer
(std::initializer_list<value_type> init, difference_type offset = 0)
: cont(init) {}
dynshifted_SequenceContainer &operator =
(std::initializer_list<value_type> init)
{cont = init; return *this;}
void assign(size_type n, value_type const &val) {return cont.assign(n, val);}
void assign(size_type n, value_type const &val, difference_type offset)
{this->offset = offset; return cont.assign(n, val);}
template<class Iterator> void assign(Iterator it, Iterator jt)
{return cont.assign(it, jt);}
template<class Iterator>
void assign(Iterator it, Iterator jt, difference_type offset)
{this.offset = offset; return cont.assign(it, jt);}
void assign(std::initializer_list<value_type> init)
{return cont.assign(init);}
void assign(std::initializer_list<value_type> init, difference_type offset)
{this.offset = offset; return cont.assign(init);}
void swap(dynshifted_SequenceContainer &that)
{std::swap(this->offset, that.offset); return cont.swap(that.cont);}
friend void swap
(dynshifted_SequenceContainer &lhs, dynshifted_SequenceContainer &rhs)
{std::swap(lhs.offset, rhs.offset); return swap(lhs.cont, rhs.cont);}
// I provide all those overloads despite the implicit conversion for
// optimisation: implicit conversion is always linear, while those
// overloads may sometimes evaluate in constant
friend bool operator ==
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont == rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator ==
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset == rhs.offset && lhs.cont == rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator ==
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset == StaticOffset && lhs.cont == rhs.cont;}
friend bool operator !=
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont != rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator !=
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset != rhs.offset || lhs.cont != rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator !=
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset != StaticOffset || lhs.cont != rhs.cont;}
friend bool operator <
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont < rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset < rhs.offset || lhs.cont < rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset < StaticOffset || lhs.cont < rhs.cont;}
friend bool operator <=
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont <= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <=
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset <= rhs.offset || lhs.cont <= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <=
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset <= StaticOffset || lhs.cont <= rhs.cont;}
friend bool operator >
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont > rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset > rhs.offset || lhs.cont > rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset > StaticOffset || lhs.cont > rhs.cont;}
friend bool operator >=
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont >= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >=
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset >= rhs.offset || lhs.cont >= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >=
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset >= StaticOffset || lhs.cont >= rhs.cont;}
constexpr size_type size() const {return cont.size();}
constexpr size_type max_size() const {return cont.max_size();}
void resize(size_type n) {return cont.resize(n);}
void resize(size_type n, value_type const &val) {return cont.resize(n, val);}
constexpr bool empty() const {return cont.empty();}
template<class ...Args> iterator emplace(const_iterator pos, Args &&...args)
{return cont.emplace(pos, std::move(args...));}
template<class ...Args> void emplace_front(Args &&...args)
{return cont.emplace_front(std::move(args...));}
template<class ...Args> void emplace_back(Args &&...args)
{return cont.emplace_back(std::move(args...));}
iterator insert(const_iterator pos, value_type const &val)
{return cont.insert(pos, val);}
iterator insert(const_iterator pos, value_type &&val)
{return cont.insert(pos, std::move(val));}
iterator insert(const_iterator pos, size_type n, value_type const &val)
{return cont.insert(pos, n, val);}
template<class Iterator> void insert(iterator pos, Iterator it, Iterator jt)
{return cont.insert(pos, it, jt);}
iterator insert(const_iterator pos, std::initializer_list<value_type> init)
{return cont.insert(pos, init);}
void push_front(value_type const &val) {return cont.push_front(val);}
void push_front(value_type &&val) {return cont.push_front(std::move(val));}
void push_back(value_type const &val) {return cont.push_back(val);}
void push_back(value_type &&val) {return cont.push_back(std::move(val));}
void clear() {return cont.clear();}
iterator erase(const_iterator pos) {return cont.erase(pos);}
iterator erase(const_iterator it, const_iterator jt)
{return cont.erase(it, jt);}
void pop_front() {return cont.pop_front();}
void pop_back() {return cont.pop_back();}
iterator begin() {return cont.begin();}
constexpr const_iterator begin() const {return cont.begin();}
constexpr const_iterator cbegin() const {return cont.cbegin();}
iterator end() {return cont.end();}
constexpr const_iterator end() const {return cont.end();}
constexpr const_iterator cend() const {return cont.cend();}
reverse_iterator rbegin() {return cont.rbegin();}
constexpr const_reverse_iterator rbegin() const {return cont.rbegin();}
constexpr const_reverse_iterator crbegin() const {return cont.crbegin();}
reverse_iterator rend() {return cont.rend();}
constexpr const_reverse_iterator rend() const {return cont.rend();}
constexpr const_reverse_iterator crend() const {return cont.crend();}
reference front() {return cont.front();}
constexpr const_reference front() const {return cont.front();}
reference back() {return cont.back();}
constexpr const_reference back() const {return cont.back();}
reference operator[] (difference_type pos)
{return cont.operator[](pos-offset);}
constexpr const_reference operator[] (difference_type pos) const
{return cont.operator[](pos-offset);}
reference at (difference_type pos)
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
constexpr const_reference at (difference_type pos) const
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
iterator find(difference_type pos)
{return cont.begin()+(pos-offset);}
constexpr const_iterator find(difference_type pos) const
{return cont.cbegin()+(pos-offset);}
};
template<class T, std::size_t N>
using dynshifted_array = dynshifted_SequenceContainer<std::array<T, N>>;
template<class T>
using dynshifted_vector = dynshifted_SequenceContainer<std::vector<T>>;
template<class T>
using dynshifted_deque = dynshifted_SequenceContainer<std::deque<T>>;
#endif
我是很喜欢这样做的,我对编程非常熟悉,所以我非常感谢你的评论。提前感谢!
发布于 2016-11-18 13:25:59
我相信你花了太多的精力去解决一些不应该成为问题的事情。简单地说,总是让容器以零为基础,例如,如果您有基于1的索引作为输入,只需在得到输入后立即从它们中减去1,并在输出之前将其转换回来。始终在内部使用基于零的索引,并且只在尽可能接近输入/输出时进行转换。
(太大了,实在评论不了)
https://codereview.stackexchange.com/questions/147365
复制相似问题