使用常量值初始化std::array

49

我需要初始化std::array中所有元素的值,就像使用std::vector可以做到的那样。

#include <vector>
#include <array>

int main()
{
  std::vector<int> v(10, 7);    // OK
  std::array<int, 10> a(7);     // does not compile, pretty frustrating
}

有没有一种优雅的方式来做到这一点?

现在我正在使用这个:

std::array<int, 10> a;
for (auto & v : a)
  v = 7;

但我希望避免使用明确的代码进行初始化。


2
关于为什么std :: array没有带有用于填充数组值的构造函数? 的问题。 - Jarod42
8个回答

39

使用 std::index_sequence,您可以执行以下操作:

namespace detail
{
    template <typename T, std::size_t ... Is>
    constexpr std::array<T, sizeof...(Is)>
    create_array(T value, std::index_sequence<Is...>)
    {
        // cast Is to void to remove the warning: unused value
        return {{(static_cast<void>(Is), value)...}};
    }
}

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

使用方法

auto a = create_array<10 /*, int*/>(7); // auto is std::array<int, 10>

std::fill解决方案相反,它可以处理非默认可构造类型。

这是我需求中最优雅的解决方案。非常类似于无法编译的 std::array<int, 10> a(7),它变成了 auto a = create_array<10, int>(7) 或者甚至是 auto a = create_array<10>(7) - Jabberwocky
所有这些与只有一个带有foreach的方法有什么区别? - Michel Feinstein
3
std::fill的解决方案不同,处理非默认构造类型 - Jarod42
所以 std::fill 是一个 foreach 吗? - Michel Feinstein
你的 foreach 或 for-range 设置值的方式相当于 fill 的作用。 - Jarod42
我明白了...嗯,我在C++方面的经验不够丰富,所以这个答案使用了很多我不熟悉的概念。 - Michel Feinstein

32

哎呀,不行;std::array支持聚合初始化但这里还不够。

幸运的是,你可以使用std::fill, 或者甚至使用std::array<T,N>::fill,从C++20开始,后者变得更加优雅,因为它成为了constexpr

参考:https://en.cppreference.com/w/cpp/container/array/fill


24

您可以按照以下步骤操作

std::array<int, 10> a; 
a.fill(2/*or any other value*/);

或者使用算法头文件中的std::fill。 要包含算法头文件,请使用

#include <algorithm>

1
感谢您抽出时间编写实际代码。这是我不经常做的事情:当有多个答案可供选择时,Stack Overflow 的效果最佳。 - Bathsheba
@Bathsheba 没问题,伙计) - h4ckthepl4net
@Bathsheba 我正在用手机写这个,所以我还没看到你已经回答了这个问题。 - h4ckthepl4net
我很高兴 - 如果你认为自己有一些话要说,可能还没有被说过,那么你应该回答一个问题。我认为这个答案在某些方面比我的更有用。 - Bathsheba

6

从C++17开始,您可以编写constexpr函数来高效设置数组,因为元素访问器现在是constexpr。这种方法也适用于各种其他设置初始值的方案:

#include <array>

template<typename T, size_t N>
constexpr auto make_array(T value) -> std::array<T, N>
{
    std::array<T, N> a{};
    for (auto& x : a)
        x = value;
    return a;
}

int main()
{
    auto arr = make_array<int, 10>(7);
}

2
需要 T 具有默认构造函数。 - Jarod42

3
如前所述,“fill”解决方案对于无法默认构造的类型不起作用。index_sequence解决方案是正确的,但有点冗长。
给定一个值(任何类型)和一个编译常量,以下代码一行求解所需:
std::apply([&](auto... dummy) {return std::array{(dummy, t)...};}, std::array<int, N>{});

完整代码请参见:https://godbolt.org/z/jcq4fqMsE

这个解决方案适用于C++17,并且可以通过一些修改应用于早期版本。


2
std::array 类型是一个支持列表初始化的聚合类型:
std::array<int, 10> a{2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

它也支持聚合初始化:

最初的回答:

它还支持聚合初始化:
std::array<int, 10> a = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

这对于长数组来说是不方便且容易出错的,对于这些情况,最好使用像Jarod42所提供的解决方案。"Original Answer"翻译成"最初的回答"。

3
对于任何数组长度来说,这都是不方便且容易出错的。 - Jabberwocky
3
@Jabberwocky 接受的答案很棒,我已经点赞了。但对于一个只有三个元素的数组来说,这种方法可能有些过度。其他方法也不适用于 constexpr std::array。所以这种技巧有时是适合的。 - Davislor

0

通过创建一个函数模板并按照下面所示返回所需的数组,这个问题可以很容易地解决。甚至可以在编译时初始化数组!(请参见答案末尾给出的c++17示例演示)。

template<std::size_t N> std::array<int, N> make_array(int val)
{
    std::array<int, N> tempArray{};    //create local array
    for(int &elem:tempArray)           //populate it 
    {
        elem = val;                     
    }
    return tempArray;                   //return it
}
int main()
{
    //---------------------V-------->number of elements  
    auto arr  = make_array<10>(7);
    //------------------------^---->value of element to be initialized with

    
    //lets confirm if all objects have the expected value 
    for(const auto &elem: arr)
    {
        std::cout << elem << std::endl; //prints all 7 
    }
    
}

演示


请注意,使用C++17甚至可以在编译时完成此操作。 演示C++17


0
从C++20开始,ranges有fill函数。
std::array<int, 10> a2;
std::ranges::fill(a2, 10);

此外,如果数组很小(对于较大的数组可能看起来奇怪),从C++17开始(参数推导)。
auto a = std::array{2,2,2,2};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接