【问题标题】:how-to initialize 'const std::vector<T>' like a c array如何像 c 数组一样初始化 'const std::vector<T>'
【发布时间】:2010-09-18 21:23:32
【问题描述】:

有没有一种优雅的方法来创建和初始化像 const T a[] = { ... } 这样的 const std::vector&lt;const T&gt; 到固定(和少量)数量的值?
我需要经常调用一个需要 vector&lt;T&gt; 的函数,但在我的情况下这些值永远不会改变。

原则上我想到了类似的东西

namespace {
  const std::vector<const T> v(??);
}

因为 v 不会在这个编译单元之外使用。

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

    对于 C++11:

    vector<int> luggage_combo = { 1, 2, 3, 4, 5 };
    

    原答案:

    您要么必须等待 C++0x,要么使用 Boost.Assign 之类的东西来做到这一点。

    例如:

    #include <boost/assign/std/vector.hpp>
    using namespace boost::assign; // bring 'operator+=()' into scope
    
    vector<int> v;
    v += 1,2,3,4,5;
    

    【讨论】:

    • “必须”可能有点强;两步法也可以。虽然 boost 方法确实不错。
    • 猜你的意思:#include
    • @Jason:boost.assign 在向量上定义了 operator+= 和 operator 的模板化重载
    • 但是如何截取每个逗号值?
    • 12345?我的行李箱上也有同样的组合!
    【解决方案2】:

    不确定我是否理解正确。我理解您的问题是这样的:您想将向量初始化为大量元素。在向量上使用push_back() 有什么问题? :-)

    如果您知道要存储的元素的数量(或确定它将存储小于 2 的下一次幂),如果您有一个 X 类型的指针向量(仅适用于指针),则可以执行此操作:

    std::vector< X* > v;
    v.reserve(num_elems);
    X* p = v.begin();
    for (int count = 0; count < num_elems; count++)
       p[count] = some_source[count];
    

    即使使用push_back(),也不要添加超过 2 次幂的元素。指向v.begin() 的指针将无效。

    【讨论】:

    • 这段代码有很多问题。您不能只调用 vector::reserve() 然后开始操作指针并依赖于魔术实现细节,例如 2 的幂。迭代器转换为指针?那么vector::size()呢?这甚至可以在什么编译器和 STL 库上编译?
    • 我同意这是非常糟糕的代码。一个最好的,你应该忘记使用指针/迭代器和回退到 push_back
    • 不,那是完全合法的 C++ 代码。 STL 保证这将起作用:vector 将为至少 2^n 个元素分配连续空间。在所有 2^n 个元素向量被允许重新分配之后,在 vector::begin() 后面给你一个新的空间。抱歉,这可能是 hacky,但这是标准。 :-)
    • ... 但是,我非常不相信使用连续内存保证通过指针添加新值是标准的 - 对于初学者来说,向量没有机会更新自己的大小,所以我很确定这段代码的结果要么是未定义的,要么是一个大小为 0 的向量。
    • 当然你可以利用向量中的连续空间;该标准保证了这一点。但是,调用 reserve() 并不足以做到这一点;您必须添加元素以使 size() 正确。您可以更改代码以便分配正确数量的元素 ....
    【解决方案3】:

    如果您要问如何初始化 const 向量以使其具有有趣的内容,那么答案可能是使用复制构造函数。首先,您费力地填写一个向量,然后从中创建新的 const 向量。或者您可以使用vector&lt;InputIterator&gt;(InputIterator, InputIterator) 构造函数模板从其他类型的容器或数组进行初始化。如果是一个数组,那么它可以用一个初始化列表来定义。

    希望这样的东西接近你想要的:

    const T ra[3] = {t1, t2, t3};
    const vector<const T> v(ra, ra+3);
    

    如果您要问如何将 const 向量传递给接受向量的函数,那么答案是:

    • 你不能,因为函数可能会改变向量并且你的对象/引用是常量。制作原始副本的非常量副本,然后将其传入。

    • 使用const_cast 删除常量,以便将其传递给一个函数,该函数采用非常量向量,但您恰好知道不会修改向量。

    后者是其中之一,完全正确的是,任何看到它的人都会对护目镜做出 cmet,而事实上他们什么都不做。这正是const_cast 的用途,但有一个相当有力的论据说,如果你需要const_cast,你已经输了。

    做这两件事(使用复制构造函数从非常量向量创建一个常量向量,然后丢弃常量)绝对是错误的——你应该只使用一个非常量向量。所以最多选择其中一项来做......

    [编辑:刚刚注意到您在谈论vector&lt;T&gt;const vector&lt;const T&gt; 之间的区别。不幸的是,在 STL 中,vector&lt;const T&gt;vector&lt;T&gt; 是完全不相关的类型,在它们之间进行转换的唯一方法是通过复制。这是向量和数组之间的区别 - T** 可以静默安全地转换为 const T *const *]

    【讨论】:

    • 如果我不想复制 t1、t2、t3,我怎样才能做到最好?这是迄今为止我发现的唯一/最佳解决方案: const int *p = &amp;ra[0];向量<const int> v; for(int i = 0; i </const>
    • @AudioDroid:是的,如果不复制它(或移动它,在 C++11 中),就无法将任何东西放入向量中,所以你会得到最接近的是指针向量(或智能指针)。但是,如果您已经将三个对象放在一个数组中,那么通常没有必要创建指向它们的指针向量:只需将该数组用于您将使用该向量的任何内容。
    【解决方案4】:

    短而脏的方式(类似于Boost的list_of()

    #include <iostream>
    #include <vector>
    #include <iterator>
    #include <algorithm>
    using namespace std;
    
    template <typename T>
    struct vlist_of : public vector<T> {
        vlist_of(const T& t) {
            (*this)(t);
        }
        vlist_of& operator()(const T& t) {
            this->push_back(t);
            return *this;
        }
    };
    
    int main() {
        const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
        copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
    }
    

    现在,C++11 有初始化列表,所以你不需要那样做,甚至不需要使用 Boost。但是,作为示例,您可以像这样在 C++11 中更有效地执行上述操作:

        #include <iostream>
        #include <vector>
        #include <utility>
        #include <ostream>
        using namespace std;
    
        template <typename T>
        struct vlist_of : public vector<T> {
            vlist_of(T&& t) {
                (*this)(move(t));
            }
            vlist_of& operator()(T&& t) {
                this->push_back(move(t));
                return *this;
            }
        };
    
        int main() {
            const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
            for (const auto& i: v) {
                cout << i << endl;
            }
        }
    

    但是,它仍然不如使用 C++11 初始化列表高效,因为没有为向量定义 operator=(vlist_of&amp;&amp;)

    tjohns20 的方式修改如下可能是更好的 c++11 vlist_of:

    #include <iostream>
    #include <vector>
    #include <utility>
    using namespace std;
    
    template <typename T>
    class vlist_of {
        public:
            vlist_of(T&& r) {
                (*this)(move(r));
            }
            vlist_of& operator()(T&& r) {
                v.push_back(move(r));
                return *this;
            }
            vector<T>&& operator()() {
                return move(v);
            }
        private:
            vector<T> v;
        
    };
    
    int main() {
        const auto v = vlist_of<int>(1)(2)(3)(4)(5)();
        for (const auto& i : v) {
            cout << i << endl;
        }
        
    }
    

    【讨论】:

    • 邪恶,但对测试等有用。++
    【解决方案5】:

    老问题,但我今天遇到了同样的问题,这是我最能接受的方法:

    vector<int> initVector(void)
    {
        vector<int> initializer;
        initializer.push_back(10);
        initializer.push_back(13);
        initializer.push_back(3);
        return intializer;
    }
    
    int main()
    {
        const vector<int> a = initVector();
        return 0;
    }
    

    避免过度复制的示例:

    vector<int> & initVector(void)
    {
        static vector<int> initializer;
        if(initializer.empty())
        {
            initializer.push_back(10);
            initializer.push_back(13);
            initializer.push_back(3);
        }
        return intializer;
    }
    
    int main()
    {
        const vector<int> & a = initVector();
        return 0;
    }
    

    【讨论】:

      【解决方案6】:

      怎么样:

      int ar[]={1,2,3,4,5,6};
      const int TotalItems = sizeof(ar)/sizeof(ar[0]);
      std::vector<int> v(ar, ar+TotalItems);
      

      【讨论】:

      • 没关系,“Steve Jessop”已经指出了这种方法。
      【解决方案7】:

      基于 Shadow2531 的响应,我使用这个类来初始化向量,而不像 Shadow 的解决方案那样实际继承 std::vector

      template <typename T>
      class vector_init
      {
      public:
          vector_init(const T& val)
          {
              vec.push_back(val);
          }
          inline vector_init& operator()(T val)
          {
              vec.push_back(val);
              return *this;
          }
          inline std::vector<T> end()
          {
              return vec;
          }
      private:
          std::vector<T> vec;
      };
      

      用法:

      std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();
      

      与 Steve Jessop 的解决方案相比,它创建了更多代码,但如果数组创建不是性能关键,我发现它是在一行中初始化数组的好方法

      【讨论】:

        【解决方案8】:

        如果它们都一样,你可以这样做

        vector<T> vec(num_items, item);
        

        但我认为它们不是——在这种情况下,最简洁的方法可能是:

        vector<T> vec(num_items);
        vec[0] = 15;
        vec[1] = 5;
        ...
        

        C++0x 将让您以您所想的方式使用初始化列表,但那是 不幸的是,现在不太好。

        【讨论】:

          【解决方案9】:

          您可以分两步完成:

          namespace {
              const T s_actual_array[] = { ... };
              const std::vector<const T> s_blah(s_actual_array,
                  s_actual_array + (sizeof(s_actual_array) / sizeof(s_actual_array[0])));
          }
          

          也许没有你想的那么漂亮,但很实用。

          【讨论】:

            【解决方案10】:

            正如其他人所说,您不能像初始化 C 样式数组一样初始化向量,除非您将指针指向源数组。但在这种情况下,如果你的向量是全局常量,为什么不直接使用旧的 C 样式数组呢?

            const int MyInts[] = {
            1, 2, 3, 4, 5};
            
            const size_t NumMyInts = sizeof(MyInts)/sizeof(MyInts[0]);
            

            您甚至可以对这个数组使用 STL 算法,就像对 const 向量使用算法一样...

            const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);
            

            【讨论】:

            • 这是一个很好的观点,但他将它传递给一个需要向量的函数。他可能无法修改该功能。
            • 这需要在外部跟踪向量的大小,这在处理许多向量时很痛苦。否则,此解决方案将是最好的解决方案,因为它只需要内存中整数的一份副本。
            猜你喜欢
            • 2011-01-26
            • 2018-10-17
            • 2013-07-25
            • 2021-06-02
            • 2015-04-30
            • 2019-01-16
            • 1970-01-01
            • 2010-11-16
            • 2019-10-20
            相关资源
            最近更新 更多