【问题标题】:Create a fixed size std::vector and write to the elements创建一个固定大小的 std::vector 并写入元素
【发布时间】:2013-03-27 18:39:42
【问题描述】:

在 C++ 中,我希望分配一个固定大小(但大小在运行时确定)std::vector,然后写入该向量中的元素。这是我正在使用的代码:

int b = 30;
const std::vector<int> test(b);
int &a = test[3];

但是,这给了我一个编译器(MSVC 2010 Pro)错误:

错误 C2440:“正在初始化”:无法从“const int”转换为“int &”。转换丢失限定符。

我对 const 的理解是,它使类的所有成员变量都成为常量。例如,以下工作正常:

class myvec
{
public:
    myvec(int num) : ptr_m(new int[num]) {};
    ~myvec() { delete ptr_m; }
    void resize(int num) { delete ptr_m; ptr_m = new int[num]; }
    int & operator[] (int i) const { return ptr_m[i]; }
    int *ptr_m;
};

const myvec test(30);
int &a = test[3]; // This is fine, as desired
test.resize(10); // Error here, as expected

因此,std::vector 似乎将容器的 const-ness 传播到向量的元素,这似乎很奇怪,因为如果我希望元素为 const,我会使用 std::vector&lt;const int&gt;。因此,这让我觉得这是 std::vector 的一个缺点。

无论如何,如何创建一个std::vector,其大小在构造后无法更改,但其元素可以写入?

【问题讨论】:

  • 首先创建变量a,然后执行最后一行。
  • @GamesBrainiac:那么a 不是参考。该错误是由于对向量中的元素有可写引用引起的,这正是我想要的。
  • 你应该尝试使用std::vector&lt;const int&gt;...看看会发生什么

标签: c++ arrays constants stdvector dynamic-arrays


【解决方案1】:

如果不编写自己的包装类,这是不可能的。如果你想使用一个普通的std::vector,你必须依靠自律,不要直接或间接使用成员函数insert()push_back()emplace_back()(例如通过back_inserter)。

请注意,目前有针对新 C++14 标准的 dynamic arrays 提案:

[...] 我们建议为数组定义一个新工具,其中 元素在构造时受到约束。我们称这些动态数组, 动态数组。

该提案实际上附带了一个参考实现,您可以在自己的代码中使用它(请确保暂时将namespace std 更改为其他内容)。

namespace std {
template< class T >
struct dynarray
{
    // types:
    typedef       T                               value_type;
    typedef       T&                              reference;
    typedef const T&                              const_reference;
    typedef       T*                              iterator;
    typedef const T*                              const_iterator;
    typedef std::reverse_iterator<iterator>       reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
    typedef size_t                                size_type;
    typedef ptrdiff_t                             difference_type;

    // fields:
private:
    T*        store;
    size_type count;

    // helper functions:
    void check(size_type n)
        { if ( n >= count ) throw out_of_range("dynarray"); }
    T* alloc(size_type n)
        { if ( n > std::numeric_limits<size_type>::max()/sizeof(T) )
              throw std::bad_array_length();
          return reinterpret_cast<T*>( new char[ n*sizeof(T) ] ); }

public:
    // construct and destruct:
    dynarray() = delete;
    const dynarray operator=(const dynarray&) = delete;

    explicit dynarray(size_type c)
        : store( alloc( c ) ), count( c )
        { size_type i;
          try {
              for ( size_type i = 0; i < count; ++i )
                  new (store+i) T;
          } catch ( ... ) {
              for ( ; i > 0; --i )
                 (store+(i-1))->~T();
              throw;
          } }

    dynarray(const dynarray& d)
        : store( alloc( d.count ) ), count( d.count )
        { try { uninitialized_copy( d.begin(), d.end(), begin() ); }
          catch ( ... ) { delete store; throw; } }

    ~dynarray()
        { for ( size_type i = 0; i < count; ++i )
              (store+i)->~T();
          delete[] store; }

    // iterators:
    iterator       begin()        { return store; }
    const_iterator begin()  const { return store; }
    const_iterator cbegin() const { return store; }
    iterator       end()          { return store + count; }
    const_iterator end()    const { return store + count; }
    const_iterator cend()   const { return store + count; }

    reverse_iterator       rbegin()       
        { return reverse_iterator(end()); }
    const_reverse_iterator rbegin()  const
        { return reverse_iterator(end()); }
    reverse_iterator       rend()         
        { return reverse_iterator(begin()); }
    const_reverse_iterator rend()    const
        { return reverse_iterator(begin()); }

    // capacity:
    size_type size()     const { return count; }
    size_type max_size() const { return count; }
    bool      empty()    const { return count == 0; }

    // element access:
    reference       operator[](size_type n)       { return store[n]; }
    const_reference operator[](size_type n) const { return store[n]; }

    reference       front()       { return store[0]; }
    const_reference front() const { return store[0]; }
    reference       back()        { return store[count-1]; }
    const_reference back()  const { return store[count-1]; }

    const_reference at(size_type n) const { check(n); return store[n]; }
    reference       at(size_type n)       { check(n); return store[n]; }

    // data access:
    T*       data()       { return store; }
    const T* data() const { return store; }
};

} // namespace std

【讨论】:

  • 感谢您发布 dynarray 实现。我也在寻求一个充分的解释来解释为什么const std::vector&lt;int&gt;const std::vector&lt;const int&gt; 的含义相同,而实际上它可能意味着 std::dynarray 的用途,而不需要新的容器。
  • @user664303 您不能将const T 放入容器中,因为这样的类型是不可分配的,因此违反了容器要求。将const 放在容器本身的前面,可以有效地禁用所有非常量成员函数,包括元素分配。这就是为什么你真的需要一个容器适配器来选择性地禁用调整成员的大小,或者像dynarray这样的特殊用途的类。
【解决方案2】:

实际的错误是因为您将向量声明为常量,这意味着您永远无法更改内容。

然后,当您尝试获取对向量中条目的非常量引用时,编译器会告诉您不能这样做,因为您可以更改存储在向量中的常量值。


至于创建一个大小可以在运行时固定,但在向量创建后不能改变大小的向量,那么你必须创建一个容器适配器。基本上你必须在另一个容器周围创建一个包装器,就像例如std::stack 会。

【讨论】:

  • const 表示我无法更改向量实例的成员变量。但是,我会认为向量的元素不是实例的成员,只是它们的指针是。指针不应指向 const 元素,因为我没有使用 std::vector。所以我觉得我不能改变元素很奇怪。
  • @user664303 通过使向量const 告诉编译器,向量的所有 都是常量,不仅大小而且其内容。
  • 显然,在 std::vector 的情况下,是的。但这不是标准行为。请参阅我更新的问题。那么为什么 std::vector 会这样做呢?特别要记住,如果我想要 const 元素,我会使用 const 类型。还没有人向我充分证明这种行为是正当的。
  • 简而言之,为什么const std::vector&lt;int&gt;const std::vector&lt;const int&gt; 的意思相同,而它可能意味着不同的意思并放弃对新的 std::dynarray 类的需求?
  • @user664303 我还没有检查标准,但是 clang 和 GCC 都抱怨它,所以我会说这是他们应该的方式。
【解决方案3】:

直接的答案是你不能这样做:你不能将向量定义为 const 然后向它​​添加成员。

正如其他人所指出的,新标准提供了数组类,这可能更适合您正在做的事情。

如果您对固定长度感兴趣,您可能感兴趣的向量中最相关的方法是reserve(),它会将vector&lt;&gt; 设置为给定参数的大小,从而无需进行向量扩展。

如果您不能使用 Std C++11,那么您需要创建一个不允许您修改向量的包装类。例如:

#include <vector>
#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;

template <typename T>
class FinalVector {
public:
    FinalVector(unsigned int size)
        { v.reserve( size ); }
    const T &at(unsigned int i) const
        { return v.at( i ); }
    T &at(unsigned int i)
        { return v.at( i ); }
    T &operator[](unsigned int i)
        { return at( i ); }
    const T &operator[](unsigned int i) const
        { return at( i ); }
    void push_back(const T &x);
    size_t size() const
        { return v.size(); }
    size_t capacity() const
        { return v.size(); }
private:
    std::vector<T> v;
};

template<typename T>
void FinalVector<T>::push_back(const T &x)
{
    if ( v.size() < v.capacity() ) {
        v.push_back( x );
    } else {
        throw runtime_error( "vector size exceeded" );
    }
}

int main()
{
    FinalVector<int> v( 3 );

    v.push_back( 1 );
    v.push_back( 2 );
    v.push_back( 3 );

    for(size_t i = 0; i < v.size(); ++i) {
        cout << v[ i ] << endl;
    }
}

希望这会有所帮助。

【讨论】:

  • 我没有添加成员。向量在构造时被分配为正确的大小。那里没问题。当我想写元素时,问题就出现了。但是,是的,看起来我需要一个包装器。
猜你喜欢
  • 2012-07-04
  • 2017-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-30
  • 2011-01-09
  • 1970-01-01
相关资源
最近更新 更多