将现有的内存用const std::vector包装起来?

15

好的,最近我了解到 (a) 标准库中的 std::vector 定义是使用连续的内存空间,因此 (b) &(v[0]) 是该连续内存块的地址,在这个地址上你可以像操作旧式 C 数组一样进行读写。就像这样...

void printem(size_t n, int* iary)
{ for (size_t i=0; i<n; ++i) std::cout << iary[i] << std::endl; }
void doublem(size_t n, int* iary)
{ for (size_t i=0; i<n; ++i) iary[i] *= 2; }

std::vector<int> v;
for (size_t i=0; i<100; ++i) v.push_back(i);
int* iptr = &(v[0]);
doublem(v.size(), iptr);
printem(v.size(), iptr);

好的,那很棒,但我想朝另一个方向发展。我有很多现有的代码,例如

double computeSomething(const std::vector<SomeClass>& v) { ... }

如果我有一个C对象数组,我可以使用如下代码:
SomeClass cary[100]; // 100*sizeof(SomeClass)
// populate this however
std::vector<SomeClass> v;
for (size_t i=0; i<100; ++i) v.push_back(cary[i]);
// now v is also using 100*sizeof(SomeClass)
double x = computeSomething(v);

我希望做到这一点(a)没有额外的空间和(b)没有插入所有那些数据的冗余副本所需的额外时间。请注意,“只是更改你愚蠢的computeSomething,白痴”是不够的,因为有成千上万个这样的函数/方法展示了这种模式,它们不在我的控制范围内,即使它们也太多了,无法全部更改。

还要注意的是,因为我只关心const std::vector&的使用,所以我的原始内存永远不需要调整大小,甚至不需要修改。 我想要像const std::vector构造函数之类的东西,但我不知道语言是否允许对类的const实例进行特殊构造,例如:

namespace std { template <typename T> class vector {
  vector() { ... }
  vector(size_t n) { ... }
  vector(size_t n, const T& t) { ... }
  const vector(size_t n, T*) { ... } // can this be done?
...

如果不可能的话,那么可以考虑从std::vector派生出一个容器,称为std::const_vector,它(a)可以从指向c数组和大小的指针构造,(b)有意地没有实现非const方法(push_back、resize等),因此即使具有typename为const_vector的对象实际上不是const对象,只提供const方法的接口也使其实际上成为了const(任何错误的修改尝试都将在编译时被捕获)。更新:一些测试表明,这种方法“解决”了我的关于std::vector在Windows实现方面的问题。
template <typename T>
class vector_tweaker : public std::vector<T> {
public:
  vector_tweaker(size_t n, T* t) {
    _saveMyfirst = _Myfirst;
    _saveMylast  = _Mylast;
    _saveMyend   = _Myend;
    _Myfirst = t;
    _Mylast  = t + n;
    _Myend   = t + n;
  }
  ~vector_tweaker() {
    _Myfirst = _saveMyfirst;
    _Mylast  = _saveMylast;
    _Myend   = _saveMyend; // and proceed to std::vector destructor
  }
private:
  T* _saveMyfirst;
  T* _saveMylast;
  T* _saveMyend;
};

当然,“解决方案”是丑陋的,因为(a)它无法防止基类通过resize()或push_back()删除原始内存(除非仅构造const vector_tweaker()并小心使用);(b)它特定于std::vector的特定实现,并且必须重新实现其他平台(如果确实只声明其std::vector成员数据为受保护的话,就像微软那样,似乎不是一个好主意)。


1
你为什么要使用C数组对象呢? - Paul J. Lucas
1
将函数更通用确实是正确的解决方案。现在你想传递一个数组,但下周可能会是std::array,再过一个月可能会是std::deque。 “C ++方式”是传递一对迭代器,虽然专家们现在正在转向范围(这实际上只是一个包含一对迭代器的结构)。 - Ben Voigt
我理解迭代器的好处,但在这种情况下,我想要一个连续的结构体{double x,y,z},我可以将其解释为行主或列主矩阵,以便发送到第三方库进行矩阵乘法等操作,并发送到所有这些现有函数,这些函数接受const std :: vector <theseobjects>&。 - RubeRad
可以通过迭代器进行下标操作(无论是随机访问迭代器) - Ben Voigt
不,你不应该data指针或&v[0]更改向量。 - Shoe
显示剩余3条评论
1个回答

2
您可以尝试使用C++11引入的std::reference_wrapper<>进行引用逻辑存储:
SomeClass cary[100];
// ...
std::vector<std::reference_wrapper<SomeClass>> cv;
cv.push_back(cary[i]);   // no object copying is done, reference wrapper is stored

如果没有C11,你可以为字节(char)创建此类模板类的专业化。然后对于来自char* C数组的构造函数,您可以使用::memcpy:不幸的是,这会使用两倍的内存。

::memcpy(&v[0], c_arr, n);

类似这样的:

template <typename T> class MyVector : public std::vector<T> {
};

template <> class MyVector<char> : public std::vector<char> {
    public:
    MyVector<char>(char* carr, size_t n) : std::vector<char>(n) {
        ::memcpy(&operator[](0), carr, n);
    }
};

我建议 - 在可能的情况下,将所有C数组替换为向量,这样就不需要额外复制。

1
是的,我知道我可以比我的示例更有效地将c_arr复制到std :: vector中,但这并不改变重复保持相同字节的两倍内存必须使用的事实。我想要的是根本不复制数据,而是在现有内存周围包装一个const std :: vector。 - RubeRad
2
专门化 std::vector<char> 将是未定义的行为。在命名空间 std 中进行的专门化需要以某种方式涉及用户定义的类型,而 char 不是这样的类型。 - Ben Voigt
@user2387508 STL容器默认情况下会复制元素,您无法更改此逻辑。您能否从一开始就将C数组替换为向量?与C数组一起工作的代码应该可以与向量一起工作。这样就不会有额外的内存复制了。 - Spock77
1
@user2387508 在这篇帖子中 - 我对上面问题的回答,我已经编辑过了。 使用 std::vector<std::reference_wrapper<SomeClass>> cv; - Spock77
1
那个 reference_wrapper 看起来非常有趣。不幸的是,我的公司在 IT 政策方面非常保守,所以我被困在 Visual Studio 2008 中。也许再过 5-10 年我才能使用 C++11! - RubeRad
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接