使更长的 std::array 像更短的一样易于访问
Make longer std::array accessible as if it's shorter
我正在实现我的静态多维向量 class。我使用 std::array
作为基础数据类型。
template <typename T, std::size_t N>
class Vector {
private:
std::array<T, N> data;
};
我想让我的 class 向下兼容,所以我这样写:
template <typename T, std::size_t N>
class Vector : public Vector<T, N-1>{
private:
std::array<T, N> data;
};
template <typename T>
class Vector<T, 0> {};
我的目标是当一个实例在向下兼容模式下使用时,它的底层数据应该能够被可靠地访问:
template<typename T, std::size_t N>
T& Vector<T, N>::operator[](int i) {
// Do boundary checking here
return this->data[i];
}
void foo(Vector<int, 3>& arg) {
arg[1] = 10;
}
Vector<int, 5> b;
foo(b);
// Now b[1] should be 10
这里有两点:
Vector<T, 5>
应该被foo()
接受,Vector<T, 2>
应该被拒绝。
foo()
中 b[0]
到 b[2]
的更改应该适用。 b[3]
和 b[4]
不应在 foo()
中访问。
我怎样才能做到这一点?
让 array
保留数据,然后创建其他非拥有 class,例如array_view
将只保留一个指针。它将具有接受数组的通用构造函数,并且将具有 static_assert
来检查大小。
std::array<>
本身的简单读取包装器怎么样?
template<typename T, std::size_t N>
struct ArrayReader {
public:
// Intentionally implicit.
template<std::size_t SRC_LEN>
ArrayReader(std::array<T, SRC_LEN> const& src)
: data_(src.data()) {
static_assert(SRC_LEN >= N);
}
private:
T const* data_;
};
void foo(ArrayReader<float, 3>);
void bar() {
std::array<float, 4> a;
std::array<float, 2> b;
foo(a);
foo(b); //BOOM!
}
当然,你可以很容易地用std::array
代替你自己的类型,这只是原理的一个例子。
以下是我的处理方法:
template <class Container, std::size_t size>
struct range_view
{
range_view(Container * p): container(p) { assert(size <= p->size()); }
auto & operator[](std::size_t i) { return (*container)[i]; }
private:
Container * container;
};
那么你只需将foo
定义为:
template <class C>
void foo(range_view<C, 3> c)
{
c[1] = 1;
}
这是最接近我认为您需要的东西。
使Vector
成为数据的viewer/user,而不是数据的所有者。
#include <array>
template <typename T, std::size_t N, std::size_t I>
class Vector : public Vector<T, N, I-1>
{
public:
Vector(std::array<T, N>& arr) : Vector<T, N, I-1>(arr), arr_(arr) {}
T& operator[](int i) {
return arr_[i];
}
private:
std::array<T, N>& arr_;
};
template <typename T, std::size_t N>
class Vector<T, N, 0ul>
{
public:
Vector(std::array<T, N>& arr) : arr_(arr) {}
private:
std::array<T, N>& arr_;
};
void foo(Vector<int, 5, 3>& arg) {
arg[1] = 10;
// Can't find a way to make this a compile time error.
arg[3] = 10;
}
#include <iostream>
int main()
{
std::array<int, 5> arr;
Vector<int, 5, 5> b(arr);
foo(b);
std::cout << b[1] << std::endl;
}
这里演示了如何实现您在问题中尝试过的 Vector
class。在每个级别,您只存储 1 个值而不是数组,这样当您将所有 N
Array
组合在一起时,您会得到 space 的 N
值。当然,调用 operator[]
会变得棘手,这是我想要演示的内容。
#include <utility>
template <class T, std::size_t N>
struct Array : Array<T, N-1>
{
T & operator[](std::size_t i)
{
return const_cast<T&>((*const_cast<const Array*>(this))[i]);
}
const T & operator[](std::size_t i) const
{
return Get(i, std::make_index_sequence<N>());
}
template <std::size_t i>
const T & Geti() const
{
return static_cast<const Array<T, i+1>&>(*this).GetValue();
}
const T & GetValue() const { return value; }
template <std::size_t ... indices>
const T & Get(std::size_t i, std::integer_sequence<std::size_t, indices...>) const
{
using X = decltype(&Array::Geti<0>);
X getters[] = { &Array::Geti<indices>... };
return (this->*getters[i])();
}
template <std::size_t i, class = typename std::enable_if<(i <= N)>::type>
operator Array<T, i>&() { return (Array<T, i>&)*this; }
private:
T value;
};
template <class T>
struct Array<T, 0>{};
void foo(Array<float, 3> & a) { a[1] = 10; }
int main()
{
Array<float, 10> a;
foo(a);
}
我正在实现我的静态多维向量 class。我使用 std::array
作为基础数据类型。
template <typename T, std::size_t N>
class Vector {
private:
std::array<T, N> data;
};
我想让我的 class 向下兼容,所以我这样写:
template <typename T, std::size_t N>
class Vector : public Vector<T, N-1>{
private:
std::array<T, N> data;
};
template <typename T>
class Vector<T, 0> {};
我的目标是当一个实例在向下兼容模式下使用时,它的底层数据应该能够被可靠地访问:
template<typename T, std::size_t N>
T& Vector<T, N>::operator[](int i) {
// Do boundary checking here
return this->data[i];
}
void foo(Vector<int, 3>& arg) {
arg[1] = 10;
}
Vector<int, 5> b;
foo(b);
// Now b[1] should be 10
这里有两点:
Vector<T, 5>
应该被foo()
接受,Vector<T, 2>
应该被拒绝。foo()
中b[0]
到b[2]
的更改应该适用。b[3]
和b[4]
不应在foo()
中访问。
我怎样才能做到这一点?
让 array
保留数据,然后创建其他非拥有 class,例如array_view
将只保留一个指针。它将具有接受数组的通用构造函数,并且将具有 static_assert
来检查大小。
std::array<>
本身的简单读取包装器怎么样?
template<typename T, std::size_t N>
struct ArrayReader {
public:
// Intentionally implicit.
template<std::size_t SRC_LEN>
ArrayReader(std::array<T, SRC_LEN> const& src)
: data_(src.data()) {
static_assert(SRC_LEN >= N);
}
private:
T const* data_;
};
void foo(ArrayReader<float, 3>);
void bar() {
std::array<float, 4> a;
std::array<float, 2> b;
foo(a);
foo(b); //BOOM!
}
当然,你可以很容易地用std::array
代替你自己的类型,这只是原理的一个例子。
以下是我的处理方法:
template <class Container, std::size_t size>
struct range_view
{
range_view(Container * p): container(p) { assert(size <= p->size()); }
auto & operator[](std::size_t i) { return (*container)[i]; }
private:
Container * container;
};
那么你只需将foo
定义为:
template <class C>
void foo(range_view<C, 3> c)
{
c[1] = 1;
}
这是最接近我认为您需要的东西。
使Vector
成为数据的viewer/user,而不是数据的所有者。
#include <array>
template <typename T, std::size_t N, std::size_t I>
class Vector : public Vector<T, N, I-1>
{
public:
Vector(std::array<T, N>& arr) : Vector<T, N, I-1>(arr), arr_(arr) {}
T& operator[](int i) {
return arr_[i];
}
private:
std::array<T, N>& arr_;
};
template <typename T, std::size_t N>
class Vector<T, N, 0ul>
{
public:
Vector(std::array<T, N>& arr) : arr_(arr) {}
private:
std::array<T, N>& arr_;
};
void foo(Vector<int, 5, 3>& arg) {
arg[1] = 10;
// Can't find a way to make this a compile time error.
arg[3] = 10;
}
#include <iostream>
int main()
{
std::array<int, 5> arr;
Vector<int, 5, 5> b(arr);
foo(b);
std::cout << b[1] << std::endl;
}
这里演示了如何实现您在问题中尝试过的 Vector
class。在每个级别,您只存储 1 个值而不是数组,这样当您将所有 N
Array
组合在一起时,您会得到 space 的 N
值。当然,调用 operator[]
会变得棘手,这是我想要演示的内容。
#include <utility>
template <class T, std::size_t N>
struct Array : Array<T, N-1>
{
T & operator[](std::size_t i)
{
return const_cast<T&>((*const_cast<const Array*>(this))[i]);
}
const T & operator[](std::size_t i) const
{
return Get(i, std::make_index_sequence<N>());
}
template <std::size_t i>
const T & Geti() const
{
return static_cast<const Array<T, i+1>&>(*this).GetValue();
}
const T & GetValue() const { return value; }
template <std::size_t ... indices>
const T & Get(std::size_t i, std::integer_sequence<std::size_t, indices...>) const
{
using X = decltype(&Array::Geti<0>);
X getters[] = { &Array::Geti<indices>... };
return (this->*getters[i])();
}
template <std::size_t i, class = typename std::enable_if<(i <= N)>::type>
operator Array<T, i>&() { return (Array<T, i>&)*this; }
private:
T value;
};
template <class T>
struct Array<T, 0>{};
void foo(Array<float, 3> & a) { a[1] = 10; }
int main()
{
Array<float, 10> a;
foo(a);
}