【问题标题】:Setup std::vector in class constructor with different values for element constructor在类构造函数中使用不同的元素构造函数值设置 std::vector
【发布时间】:2015-08-01 13:53:24
【问题描述】:

我有一个带有非平凡构造函数的类:

class mystream
{
public:
    mystream(size_t buffersize,size_t index) : buffersize(buffersize),index(index){}

    size_t buffersize;
    size_t index;
};

mystream 实例有一个唯一的 id,它与它在管理类向量中的位置相对应:

class mystreammanager
{
public:
   mystreammanager() : streams(8,1024, /* WHAT TO DO HERE ??? */ )
   {
   }

  std::vector<mystream> streams;
};

如何构造向量并使用索引的升序值初始化其元素?

【问题讨论】:

    标签: c++ c++11 vector constructor


    【解决方案1】:

    一种简洁、清晰且易于调试的方法是将向量的构造推迟到 static 类函数:

    class mystreammanager
    {
    public:
       mystreammanager() : streams{ generate_streams(1024, 8) }
       {
       }
    
    private:
      static std::vector<mystream> generate_streams(size_t buffersize, size_t qty)
      {
        std::vector<mystream> result;
        result.reserve(qty);
        for(size_t i = 0 ; i < qty ; ++qty) {
          result.emplace_back(buffersize, i); 
        }
        return result;
      } 
    
      std::vector<mystream> streams;
    };
    

    这是最有效的,因为:

    1. RVO 导致向量就地构建
    2. 避免列表初始化意味着没有冗余副本。

    【讨论】:

    • 这很棒,但如果您在团队中工作,则无法使用,因为普通开发人员不希望在初始化期间对实例本身进行方法调用(至少,我不希望) .但是谢谢
    • @MartinSchlott:如果您的团队无法读取简单的函数,那么您需要一个新团队。
    • @MikeSeymour 也许你是对的。但是一个人必须使用他所拥有的东西:-)。问题是恕我直言,当您在管理课程中必须特别注意一个以上向量时,这种情况可能会升级。我喜欢思考发生在附近。此外,与循环 AFAIK 相比,它并没有真正的优势。
    • @MartinSchlott 如果你有多个你需要特别注意的向量,那么我认为这更有理由将构建推迟到一个单独的函数。
    • 将构造延迟到函数的优点是 RVO 可以为您提供最佳性能,而逻辑只编写一次(例如,如果您有 2 个重载的构造函数)。
    【解决方案2】:

    只需使用循环:

    mystreammanager() {
        streams.reserve(8);
        for (int i = 0; i < 8; ++i) {
            streams.emplace_back(1024, i);
        }
    }
    

    【讨论】:

    • 这就是我想要避免的。
    • @MartinSchlott 为什么?这是最清晰的方法。
    • 最后,我会按照你的建议去做,因为这确实是最清晰的方法。但我不得不接受@Jarod42 的回答,因为它是我问题的确切答案,但也显示出令人不快的效果。
    【解决方案3】:

    编译时版本:)

    需要c++14,但肯定可以适应c++11

    #include <cstddef>
    #include <vector>
    #include <utility>
    
    class mystream
    {
    public:
      mystream(size_t buffersize,size_t index) : buffersize(buffersize),index(index){}
    
      size_t buffersize;
      size_t index;
    };
    
    template<size_t... Indexes>
    std::initializer_list<mystream>    mystream_maker_impl(std::index_sequence<Indexes...>)
    {
      return {{1024, Indexes}...};
    }
    
    template<size_t N>
    std::initializer_list<mystream>    mystream_maker()
    {
      return mystream_maker_impl(std::make_index_sequence<N>());
    }
    
    class mystreammanager
    {
    public:
      mystreammanager() : streams(mystream_maker<8>())
      {
      }
    
      std::vector<mystream> streams;
    };
    

    【讨论】:

    • 太棒了。你能告诉我哪个部分是 C++14 吗?我不能接受这个答案,因为我在我的问题中提到了 C++11。
    • std::index_sequencestd::make_index_sequence (en.cppreference.com/w/cpp/utility/integer_sequence) 是 C++14,但除非我的想象力在欺骗我,否则我已经在 stackoverflow 上多次看到了这些实现。
    • 我在最新的 XCode 上对其进行了测试,它只接受使用 -std=C++14 或 -std=gnu++14 而不是 c++11 进行编译。因为我也使用 VS2013,所以我不希望它能够在我的所有平台上编译。除此之外,这将是我正在寻找的解决方案,尽可能在编译时解决方案。
    • @MartinSchlott 正如我在之前的评论中所说,如果你想在 c++11 上编译它,你必须重新实现 std::index_sequencestd::make_index_sequence,看看 stackoverflow.com/questions/17424477/… :)跨度>
    • 不幸的是 std::initializer_list 不能感知移动,所以 stream 类型需要是可复制的。在我看来,这是std::initializer_list设计中的一个严重疏忽
    【解决方案4】:

    你可以这样做:

    class mystreammanager
    {
    public:
       mystreammanager() : streams{{1024, 0}, {1024, 1}, {1024, 2}, {1024, 3},
                                   {1024, 4}, {1024, 5}, {1024, 6}, {1024, 7}}
       {
       }
    
      std::vector<mystream> streams;
    };
    

    但是做一个循环似乎更安全/更简单。

    【讨论】:

      【解决方案5】:

      我使用了@RichardHodges 的答案,因为我对我的第一选择不满意。 我想出了这个模板:

      template<class T,class ...Args> std::vector<T> generate_with_index(size_t qty,Args ...args)
      {
          std::vector<T> result;
          result.reserve(qty);
          for(size_t i = 0 ; i < qty ; ++qty)
              result.emplace_back(i, args...);
          return result;
      }
      

      它帮助我避免冗余。从理论上讲,我最喜欢@Drax 解决方案,因为它在编译期间完成的工作最多。

      【讨论】:

      • 在这种情况下,您需要像这样消除冗余副本:template&lt;class T,class ...Args&gt; std::vector&lt;T&gt; generate_with_index(size_t qty,Args&amp;&amp; ...args) { std::vector&lt;T&gt; result; result.reserve(qty); for(size_t i = 0 ; i &lt; qty ; ++qty) result.emplace_back(i, std::forward&lt;Args&gt;(args)...); return result; }
      猜你喜欢
      • 2012-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多