不可调整大小的向量/不可重新分配但可变成员的数组?

发布于 2024-09-07 15:37:50 字数 1666 浏览 1 评论 0原文

有没有办法制作不可重新分配但可变成员的不可调整大小的向量/数组?我能想象到的最接近的事情是使用 vectorconst 副本是从临时构造的,但由于我在初始化时知道​​有多少个以及到底想要什么,所以我宁愿拥有一个对象块而不是指针。是否可以使用 std::vector 或一些更晦涩的 boost 等模板来实现如下所示的内容?

// Struct making vec<A> that cannot be resized or have contents reassigned.
struct B {
  vector<A> va_; // <-- unknown modifiers or different template needed here
  vector<A> va2_;

  // All vector contents initialized on construction.
  Foo(size_t n_foo) : va_(n_foo), va2_(5) { }

  // Things I'd like allowed: altering contents, const_iterator and read access.
  good_actions(size_t idx, int val) {
    va_[idx].set(val);

    cout << "vector<A> info - " <<  " size: " << va_.size() << ", max: "
      << va_.max_size() << ", capacity: " << va_.capacity() << ", empty?: "
      << va_.empty() << endl;

    if (!va_.empty()) {
      cout << "First (old): " << va_[0].get() << ", resetting ..." << endl;
      va_[0].set(0);
    }

    int max = 0;
    for (vector<A>::const_iterator i = va_.begin(); i != va_.end(); ++i) {
      int n = i->get();
      if (n > max) { max = n; }
      if (n < 0)   { i->set(0); }
    }
    cout << "Max : " << max << "." << endl;
  }

  // Everything here should fail at compile.
  bad_actions(size_t idx, int val) {
    va_[0]    = va2_[0];
    va_.at(1) = va2_.at(3);

    va_.swap(va2_);
    va_.erase(va_.begin());
    va_.insert(va_.end(), va2_[0]);

    va_.resize(1);
    va_.clear();
    // also: assign, reserve, push, pop, .. 
  }
};

Is there a way to make a non-resizeable vector/array of non-reassignable but mutable members? The closest thing I can imagine is using a vector<T *> const copy constructed from a temporary, but since I know at initialization how many of and exactly what I want, I'd much rather have a block of objects than pointers. Is anything like what is shown below possible with std::vector or some more obscure boost, etc., template?

// Struct making vec<A> that cannot be resized or have contents reassigned.
struct B {
  vector<A> va_; // <-- unknown modifiers or different template needed here
  vector<A> va2_;

  // All vector contents initialized on construction.
  Foo(size_t n_foo) : va_(n_foo), va2_(5) { }

  // Things I'd like allowed: altering contents, const_iterator and read access.
  good_actions(size_t idx, int val) {
    va_[idx].set(val);

    cout << "vector<A> info - " <<  " size: " << va_.size() << ", max: "
      << va_.max_size() << ", capacity: " << va_.capacity() << ", empty?: "
      << va_.empty() << endl;

    if (!va_.empty()) {
      cout << "First (old): " << va_[0].get() << ", resetting ..." << endl;
      va_[0].set(0);
    }

    int max = 0;
    for (vector<A>::const_iterator i = va_.begin(); i != va_.end(); ++i) {
      int n = i->get();
      if (n > max) { max = n; }
      if (n < 0)   { i->set(0); }
    }
    cout << "Max : " << max << "." << endl;
  }

  // Everything here should fail at compile.
  bad_actions(size_t idx, int val) {
    va_[0]    = va2_[0];
    va_.at(1) = va2_.at(3);

    va_.swap(va2_);
    va_.erase(va_.begin());
    va_.insert(va_.end(), va2_[0]);

    va_.resize(1);
    va_.clear();
    // also: assign, reserve, push, pop, .. 
  }
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

策马西风 2024-09-14 15:37:50

你的要求有问题。但首先让我们解决固定大小问题,它被称为 std::tr1::array (如果您在编译时知道大小)。

如果您在编译时不知道,您仍然可以在向量上使用一些代理类。

template <class T>
class MyVector
{
public:
  explicit MyVector(size_t const n, T const& t = T()): mVector(n,t) {}

  // Declare the methods you want here
  // and just forward to mVector most of the time ;)

private:
  std::vector<T> mVector;
};

然而,如果你是可变的,那么不可分配有什么意义呢?没有什么可以阻止用户做繁重的工作:

class Type
{
public:
  int a() const { return a; }
  void a(int i) { a = i; }

  int b() const { return b; }
  void b(int i) { b = i; }
private:
  Type& operator=(Type const&);

  int a, b;
};

没有什么可以阻止我做:

void assign(Type& lhs, Type const& rhs)
{
  lhs.a(rhs.a());
  lhs.b(rhs.b());
}

我只是想打你的头,让我的生活变得复杂......

也许你能更准确地描述你想做的事情吗?限制类上可能操作的子集(某些变量不应该修改,但其他变量可以)?

在这种情况下,您可以再次使用 Proxy 类。

class Proxy
{
public:
  // WARN: syntax is screwed, but `vector` requires a model
  // of the Assignable concept so this operation NEED be defined...
  Proxy& operator=(Proxy const& rhs)
  {
    mType.a = rhs.mType.a;
    // mType.b is unchanged
    return *this;
  }

  int a() const { return mType.a(); }
  void a(int i) { mType.a(i); }      

  int b() const { return mType.b(); }

private:
  Type mType;
};

使用合适的代理没有什么不能做的。这也许是我见过的最有用的模式。

There is an issue with your requirements. But first let's tackle the fixed size issue, it's called std::tr1::array<class T, size_t N> (if you know the size at compile time).

If you don't know it at compile time, you can still use some proxy class over a vector.

template <class T>
class MyVector
{
public:
  explicit MyVector(size_t const n, T const& t = T()): mVector(n,t) {}

  // Declare the methods you want here
  // and just forward to mVector most of the time ;)

private:
  std::vector<T> mVector;
};

However, what is the point of not being assignable if you are mutable ? There is nothing preventing the user to do the heavy work:

class Type
{
public:
  int a() const { return a; }
  void a(int i) { a = i; }

  int b() const { return b; }
  void b(int i) { b = i; }
private:
  Type& operator=(Type const&);

  int a, b;
};

Nothing prevents me from doing:

void assign(Type& lhs, Type const& rhs)
{
  lhs.a(rhs.a());
  lhs.b(rhs.b());
}

I just want to hit you on the head for complicating my life...

Perhaps could you describe more precisely what you want to do, do you wish to restrict the subset of possible operations on your class (some variables should not be possible to modify, but other could) ?

In this case, you could once again use a Proxy class

class Proxy
{
public:
  // WARN: syntax is screwed, but `vector` requires a model
  // of the Assignable concept so this operation NEED be defined...
  Proxy& operator=(Proxy const& rhs)
  {
    mType.a = rhs.mType.a;
    // mType.b is unchanged
    return *this;
  }

  int a() const { return mType.a(); }
  void a(int i) { mType.a(i); }      

  int b() const { return mType.b(); }

private:
  Type mType;
};

There is not much you cannot do with suitable proxies. That's perhaps the most useful pattern I have ever seen.

你的笑 2024-09-14 15:37:50

你所问的实际上是不可能的。

防止分配某些内容的唯一方法是将该类型的operator = 定义为private。 (作为其扩展,由于 const operator = 方法没有多大意义(因此不常见),您可以通过仅允许从容器访问 const 引用来接近这一点。但是用户仍然可以定义一个 const operator =,并且无论如何你都需要可变对象。)

如果你考虑一下,std::vector::operator [] 返回一个引用它所包含的价值。使用赋值运算符将调用 operator = 来获取值。 std::vector 在这里被完全绕过(除了用于首先获取引用的 operator[] 调用),因此不可能(>std::vector) 以任何方式覆盖对 operator = 函数的调用。

您为直接访问容器中对象的成员所做的任何操作都必须返回对该对象的引用,然后可以使用该引用来调用该对象的operator =。因此,容器无法阻止其内部的对象被分配,除非容器为其包含的对象实现一个代理,该代理具有一个私有赋值运算符,该运算符不执行任何操作并将其他调用转发到“真实”对象,但不允许直接访问真实对象(尽管如果这样做有意义,您可以返回真实对象的副本)。

What you're asking is not really possible.

The only way to prevent something from being assigned is to define the operator = for that type as private. (As an extension of this, since const operator = methods don't make much sense (and are thus uncommon) you can come close to this by only allowing access to const references from your container. But the user can still define a const operator =, and you want mutable objects anyways.)

If you think about it, std::vector::operator [] returns a reference to the value it contains. Using the assignment operator will call operator = for the value. std::vector is completely bypassed here (except for the operator[] call used to get the reference in the first place) so there is no possibility for it (std::vector) to in any way to override the call to the operator = function.

Anything you do to directly access the members of an object in the container is going to have to return a reference to the object, which can then be used to call the object's operator =. So, there is no way a container can prevent objects inside of it from being assigned unless the container implements a proxy for the objects it contains which has a private assignment operator that does nothing and forwards other calls to the "real" object, but does not allow direct access to the real object (though if it made sense to do so, you could return copies of the real object).

薄情伤 2024-09-14 15:37:50

您能否创建一个包含对对象的引用的类,但其构造函数只能由其 std::vector 的朋友访问?

例如:

template<typename T>
class MyRef {
   firend class std::vector< MyRef<T> >
public:
   T& operator->();
[...etc...]

Could you create a class which holds a reference to your object, but its constructors are only accessible to its std::vector's friend?

e.g.:

template<typename T>
class MyRef {
   firend class std::vector< MyRef<T> >
public:
   T& operator->();
[...etc...]
最丧也最甜 2024-09-14 15:37:50

您可以通过创建 std::vector const 和向量的 structclass 数据来实现您想要的目的可变。您的 set 方法必须是 const。下面是一个在 g++ 中按预期工作的示例:

#include <vector>

class foo
{ 
public:
  foo () : n_ () {}
  void set(int n) const { n_ = n; }

private:

  mutable int n_;
};

int main()
{
  std::vector<foo> const a(3);  // Notice the "const".
  std::vector<foo> b(1);

  // Executes!
  a[0].set(1);

  // Failes to compile!
  a.swap(b);
}

这样您就不能以任何方式更改 vector,但您可以修改由向量持有的对象的 mutable 数据成员向量。下面是这个例子的编译方式:

g++ foo.cpp 
foo.cpp: In function 'int main()':
foo.cpp:24: error: passing 'const std::vector<foo, std::allocator<foo> >' as 'this' argument of 'void std::vector<_Tp, _Alloc>::swap(std::vector<_Tp, _Alloc>&) [with _Tp = foo, _Alloc = std::allocator<foo>]' discards qualifiers

我能想到的一个缺点是你必须更加了解 const-正确性 代码,但这也不一定是缺点。

哈!

编辑/澄清:这种方法的目标不是完全击败const。相反,目标是演示使用标准 C++ 和 STL 实现 OP 问题中提出的要求的方法。它不是理想的解决方案,因为它公开了一个 const 方法,允许更改用户可见的内部状态。当然,这是这种方法的问题。

You can achieve what you want by making the std::vector const, and the vector's struct or class data mutable. Your set method would have to be const. Here's an example that works as expected with g++:

#include <vector>

class foo
{ 
public:
  foo () : n_ () {}
  void set(int n) const { n_ = n; }

private:

  mutable int n_;
};

int main()
{
  std::vector<foo> const a(3);  // Notice the "const".
  std::vector<foo> b(1);

  // Executes!
  a[0].set(1);

  // Failes to compile!
  a.swap(b);
}

That way you can't alter the vector in any way but you can modify the mutable data members of the objects held by the vector. Here's how this example compiles:

g++ foo.cpp 
foo.cpp: In function 'int main()':
foo.cpp:24: error: passing 'const std::vector<foo, std::allocator<foo> >' as 'this' argument of 'void std::vector<_Tp, _Alloc>::swap(std::vector<_Tp, _Alloc>&) [with _Tp = foo, _Alloc = std::allocator<foo>]' discards qualifiers

The one disadvantage I can think of is that you'll have to be more aware of the const-correctness of your code, but that's not necessarily a disadvantage either.

HTH!

EDIT / Clarification: The goal of this approach is not defeat const completely. Rather, the goal is to demonstrate a means of achieving the requirements set forth in the OP's question using standard C++ and the STL. It is not the ideal solution since it exposes a const method that allows alteration of the internal state visible to the user. Certainly that is a problem with this approach.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文