【问题标题】:std::array initialization, for objects without a default ctor?std::array 初始化,对于没有默认 ctor 的对象?
【发布时间】:2020-09-09 22:50:20
【问题描述】:

我在一个内存受限的系统上工作,其中 new/malloc 调用在构建时被包装以失败。因此,很遗憾,std::vector 不是一个可接受的解决方案。

我有一个类的 std::array 成员,其大小在编译时已知,但在特定目标之间可能会有所不同(例如,来自配置文件),因此我可以访问类似 constexpr size_t len = Config::ArrSize 的内容。我希望我的班级拥有std::array 的对象,但这些对象没有默认构造函数。我更愿意避免两步初始化(例如,实现一个毫无意义的默认 ctor,然后稍后传递它们的实际值)。我也知道编译时的所有构造函数值!我只是找不到一种干净的方式来传达这一点,因为 length 可能在特定目标之间有所不同,但对于任何 given garget 都是已知的。

有没有办法清晰地传达这一点?例如我想要类似的东西

#include Config.h

constexpr size_t arr_size = Config::ArrSize;
constexpr size_t ctor_arg = Config::Arg;

class Foo {
  public:
  // line which doesn't work but demonstrates what I'd like
  Foo() : fooArr {Bar(ctor_arg)} {}
  private:
  std::array<Bar, arr_size> fooArr;

};

它们都将以相同的方式初始化,并且在编译时具有已知的大小。由于缺少默认的ctors,它们必须在初始化时构建。类似于std::fill 但在初始化时可用。是的,我可以用指针将它推迟一段时间,直到 ctor 主体,但恕我直言。我该怎么做?

Bar 没有 constexper ctor,但也许这可能会有所帮助?

【问题讨论】:

  • 在Config.cpp中实现Foo的默认构造函数是否可行? coliru.stacked-crooked.com/a/bbbcba21c164cbdd
  • @MooingDuck:这可以避免这个问题,但很好!通常最好一开始就避免这个问题。
  • @MooingDuck 是的,在我的特定用例中是可能的,但我认为这个问题应该代表那些没有能力修改他们可能必须使用的代码的人。
  • @brenzo:我的后续建议是将#define CTOR_IMPL {Bar(ctor_arg), Bar(ctor_arg), Bar(ctor_arg)} 放在标题中

标签: c++ initialization


【解决方案1】:

选项 1:使用命名构造函数

我们将编写一个named constructor idiomarray_repeat(),它采用单个值并在编译时生成一个包含所有值的数组。然后你可以写:

class Foo {
public:
  Foo() : fooArr {array_repeat<arr_size>(Bar(ctor_arg))} {}
private:
  std::array<Bar, arr_size> fooArr;
};

我已将实现拆分为一个单独的问题和答案,here on SO

选项 2:使用带有自定义分配器的向量

std::vector has 一个额外的模板参数 - 分配器类。默认值为使用new[]delete[] 的分配器。但是 - 你可以有一个分配器,它在堆栈(或任何地方)上占用一个固定的缓冲区,并且仍然使用一个可调整大小的std::vector。这样您就无需预先构建虚拟值。

【讨论】:

  • 只是想知道,您是如何让链接看起来像一个按钮的?
  • @Ranoiaetep:点击我帖子下方的“编辑”按钮,通过阅读源代码找出答案!
  • 一直在尝试,一直显示“编辑队列已满”:D
  • @Ranoiaetep:在链接括号内尝试
  • 酷!谢谢!
【解决方案2】:

您可以使用大括号初始化std::array&lt;int,4&gt; a = {1,2,3,4};...也许您可以编写一个模板函数来初始化它而无需直接编写初始化列表,但我不确定如何。

一般来说,当类型不可默认构造时,我不建议使用std::array。您可以使用 boost::small_vector 之类的类型来代替具有静态容量和动态大小的类型。

【讨论】:

  • 遗憾的是,我们不使用boost,我怀疑我是否可以为这么小的变化提供理由。大括号初始化可以工作,但我不能使它适用于大小N。我需要一种方法来使用标准库 afaik。
  • @brenzo 您可以复制实现或制作类似的东西。这门课一点也不复杂。
【解决方案3】:

你可以这样做:

class Bar
{
public:
    Bar(someClass obj) { /*...*/ }
};

class Foo
{
public:
    template<class... T>
    Foo(T... obj) : fooArr {Bar(obj)...} {}
private:
    std::array<Bar, arr_size> fooArr;
}

虽然我不知道这是否是最好的方法。

【讨论】:

  • 这个问题是我相信我必须在整个Foo 类中添加模板注释,这又真的很混乱。 :/例如在.cpp 文件中的十几个方法上会有template&lt;&gt; 语句。
  • 啊,抱歉,看到您刚刚修改了答案。这可能是可行的。
  • @brenzo 我相信你只需要构造函数的模板,因为你的 std::array 已经有一个类型。
  • @brenzo 没问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-18
  • 2019-07-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
相关资源
最近更新 更多