【问题标题】:Why does std::array not have an constructor that takes a value for the array to be filled with?为什么 std::array 没有一个构造函数来为要填充的数组取一个值?
【发布时间】:2013-07-29 12:11:32
【问题描述】:

是缺席

std::array<T,size>::array(const T& value);

疏忽?它对我来说似乎很有用,动态容器(如std::vector)确实有一个类似的构造函数。

我很清楚

std::array<T,size>::fill(const T& value);

但这不是构造函数,内存会先清零。如果我想要所有 -1 之类的 this guy 怎么办?

【问题讨论】:

  • “内存会先清零”你确定这是真的吗?
  • 它不会先被清零,除非你要求它。
  • 除了所有答案中的 aggregate 参数外,还可能存在更多概念性的推理。填充构造函数可能会隐藏它并没有真正构造单个元素的事实。它将首先调用聚合初始化,然后然后将值复制到元素中,它不能立即复制构造元素(相比之下,std::vector)。因此,由于它总是等价于array(); array.fill();,所以首先省略构造函数并不能隐藏这个事实。

标签: c++ c++11 stdarray


【解决方案1】:

std::array 在设计上是一个聚合,因此没有用户声明的构造函数。

正如您所说,您可以在默认构造后使用fill。由于它是一个聚合,默认构造不会将内存归零,但会使其未初始化(如果包含的类型可以简单地初始化)。

【讨论】:

  • 所以this page 错了吗?它明确表示数组的元素是默认初始化的。
  • 默认初始化对于 POD 来说是 no-init,而对于我认为的其他一切都是默认构造函数 - 取决于声明点。
  • @rubenvb:是的,它们将被默认初始化,而不是值初始化。因此,如果它们可以简单地初始化,那么它们将不会被初始化。
  • 啊,是的。这仍然是标准 IMO 中非常顽皮的部分,它区分了用户类型和内置类型 :/
  • @rubenvb 原始类型都有微不足道的默认初始化。如果需要,用户定义的类型可以以相同的方式运行。这不是区别,而是一致性。
【解决方案2】:

请注意,您可以利用数组不是零初始化的事实有效地模拟这种类型的构造函数,并且具有复制构造函数和执行。

template <size_t N, class T>
array<T,N> make_array(const T &v) {
    array<T,N> ret;
    ret.fill(v);
    return ret;
}

auto a = make_array<20>('z');

【讨论】:

  • 我们不要用不需要的模板参数;-)来膨胀调用站点。
  • char 可以推断出来,所以你可以写make_array&lt;20&gt;('z') 而不是make_array&lt;20,char&gt;('z')
  • @Nawaz 哦,更好。我应该问为什么没有make_array 而不是:-)
  • @Walter:您无法返回任何类型的引用,因为您将返回对局部变量的引用。
  • T 不是默认可构造函数时,这是行不通的,这时你非常需要填充构造函数。
【解决方案3】:

您可以为此使用std::index sequence

namespace detail
{

    template <typename T, std::size_t...Is>
    constexpr std::array<T, sizeof...(Is)>
    make_array(const T& value, std::index_sequence<Is...>)
    {
        return {{(static_cast<void>(Is), value)...}};
    }
}

template <std::size_t N, typename T>
constexpr std::array<T, N> make_array(const T& value)
{
    return detail::make_array(value, std::make_index_sequence<N>());
}

Demo

std::make_index_sequence 是 C++14,但可以在 C++11 中实现。

static_cast&lt;void&gt;(Is) 用于处理 T 可能提供的邪恶 operator,

【讨论】:

  • 这是最有用的答案,它也适用于T 不可默认构造的情况,即其他答案不足的情况。
  • @ChrisBeck 完全同意。这正是我的情况,我对其他答案感到沮丧。表明赞成票不是一切。
  • 由于使用模板递归,这不会为大型数组编译。如果我需要一个没有默认构造函数的 T 的百万元素数组,我会不走运吗?即使是 C 数组也适合我。
  • @MichałBrzozowski:std::index_sequence 可以用对数实例化而不是线性实现。并且编译器可能只有 "intrinsics" 来进行一次实例化。然后,不再有递归。
  • @MichałBrzozowski:你仍然可以在循环中切换到std::vectorreserveemplace_back。对于一百万个元素,堆栈在实践中会出现内存限制的问题.
【解决方案4】:

首先,不是std::array&lt;T&gt;,而是std::array&lt;T,N&gt;,其中N是编译时间常数积分表达式。​​

其次,std::array 被设计为聚合。所以它没有任何使它成为非聚合的东西,这就是为什么它没有构造函数......和析构函数,虚函数等。

【讨论】:

    【解决方案5】:

    我从Jarod42 那里得到答案,并进行了扩展,以便能够使用可变数量的构造函数参数,并添加了自动索引器作为第一个参数:

    namespace detail {
      template <typename T, std::size_t... Seq, typename... Args>
      constexpr std::array<T, sizeof...(Seq)> make_array(std::index_sequence<Seq...>, Args &... args)
      {
        return {{(static_cast<void>(Seq), T(Seq, args...))...}};
      }
    }  // namespace detail
    
    template <typename T, std::size_t N, typename... Args>
    constexpr std::array<T, N> make_array(Args &... args)
    {
      return detail::make_array<T>(std::make_index_sequence<N>(), args...);
    }
    
    class myClass {
      myClass(unsigned int i, float a, std::string b, int c):... {};
    }
    
    

    用法:

    auto myArray = make_array<myClass, 64>(a, b, c);
    

    【讨论】:

      猜你喜欢
      • 2022-01-12
      • 2020-12-28
      • 1970-01-01
      • 2020-03-22
      • 2021-10-12
      • 2012-03-05
      • 1970-01-01
      • 2017-03-13
      • 2023-04-09
      相关资源
      最近更新 更多