数组初始化有一个很好的数组初始化列表。我有一个
int array[100] = {-1};
期望它充满-1,但它不是,只有第一个值是,其余都是0和随机值的混合。
的代码
int array[100] = {0};
工作很好,并将每个元素设置为0。
我错过了什么…如果值不为零,就不能初始化它吗?
和2:是默认的初始化(如上所述)比通常的循环通过整个数组和赋值还是做同样的事情?
数组初始化有一个很好的数组初始化列表。我有一个
int array[100] = {-1};
期望它充满-1,但它不是,只有第一个值是,其余都是0和随机值的混合。
的代码
int array[100] = {0};
工作很好,并将每个元素设置为0。
我错过了什么…如果值不为零,就不能初始化它吗?
和2:是默认的初始化(如上所述)比通常的循环通过整个数组和赋值还是做同样的事情?
当前回答
使用std::array,我们可以在c++ 14中以相当直接的方式做到这一点。这只能在c++ 11中实现,但是稍微复杂一些。
我们的接口是一个编译时大小和默认值。
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
第三个函数主要是为了方便,所以用户不必自己构造std::integral_constant<std::size_t, size>,因为那是一个相当冗长的构造。真正的工作是由前两个函数中的一个完成的。
第一个重载非常简单:它构造了一个大小为0的std::数组。没有复制的必要,我们只是构造它。
第二个过载有点棘手。它沿着它得到的值作为源进行转发,它还构造了make_index_sequence的一个实例,并调用其他一些实现函数。这个函数是什么样的呢?
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
This constructs the first size - 1 arguments by copying the value we passed in. Here, we use our variadic parameter pack indexes just as something to expand. There are size - 1 entries in that pack (as we specified in the construction of make_index_sequence), and they have values of 0, 1, 2, 3, ..., size - 2. However, we do not care about the values (so we cast it to void, to silence any compiler warnings). Parameter pack expansion expands out our code to something like this (assuming size == 4):
return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };
我们使用这些括号来确保可变的包扩展…展开我们想要的内容,并确保我们使用了逗号操作符。如果没有括号,看起来就像我们在给数组初始化传递一堆参数,但实际上,我们是在计算索引,将它强制转换为void,忽略void结果,然后返回value,它被复制到数组中。
最后一个参数,我们称之为std::forward on,是一个小优化。如果有人传入一个临时的std::string并说“创建一个5个这样的数组”,我们希望有4个副本和1个移动,而不是5个副本。std::forward确保我们这样做。
完整的代码,包括头文件和一些单元测试:
#include <array>
#include <type_traits>
#include <utility>
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
struct non_copyable {
constexpr non_copyable() = default;
constexpr non_copyable(non_copyable const &) = delete;
constexpr non_copyable(non_copyable &&) = default;
};
int main() {
constexpr auto array_n = make_array_n<6>(5);
static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");
constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");
constexpr auto array_empty = make_array_n<0>(2);
static_assert(array_empty.empty(), "Incorrect array size for empty array.");
constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
其他回答
对于单字节元素数组,可以使用memset将所有元素设置为相同的值。
这里有一个例子。
gcc编译器有一个扩展,允许以下语法:
int array[100] = { [0 ... 99] = -1 };
这将把所有元素都设为-1。
这被称为“指定初始化器”,更多信息请参见这里。
注意,这不是gcc c++编译器实现的。
使用std::array,我们可以在c++ 14中以相当直接的方式做到这一点。这只能在c++ 11中实现,但是稍微复杂一些。
我们的接口是一个编译时大小和默认值。
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
第三个函数主要是为了方便,所以用户不必自己构造std::integral_constant<std::size_t, size>,因为那是一个相当冗长的构造。真正的工作是由前两个函数中的一个完成的。
第一个重载非常简单:它构造了一个大小为0的std::数组。没有复制的必要,我们只是构造它。
第二个过载有点棘手。它沿着它得到的值作为源进行转发,它还构造了make_index_sequence的一个实例,并调用其他一些实现函数。这个函数是什么样的呢?
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
This constructs the first size - 1 arguments by copying the value we passed in. Here, we use our variadic parameter pack indexes just as something to expand. There are size - 1 entries in that pack (as we specified in the construction of make_index_sequence), and they have values of 0, 1, 2, 3, ..., size - 2. However, we do not care about the values (so we cast it to void, to silence any compiler warnings). Parameter pack expansion expands out our code to something like this (assuming size == 4):
return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };
我们使用这些括号来确保可变的包扩展…展开我们想要的内容,并确保我们使用了逗号操作符。如果没有括号,看起来就像我们在给数组初始化传递一堆参数,但实际上,我们是在计算索引,将它强制转换为void,忽略void结果,然后返回value,它被复制到数组中。
最后一个参数,我们称之为std::forward on,是一个小优化。如果有人传入一个临时的std::string并说“创建一个5个这样的数组”,我们希望有4个副本和1个移动,而不是5个副本。std::forward确保我们这样做。
完整的代码,包括头文件和一些单元测试:
#include <array>
#include <type_traits>
#include <utility>
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
struct non_copyable {
constexpr non_copyable() = default;
constexpr non_copyable(non_copyable const &) = delete;
constexpr non_copyable(non_copyable &&) = default;
};
int main() {
constexpr auto array_n = make_array_n<6>(5);
static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");
constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");
constexpr auto array_empty = make_array_n<0>(2);
static_assert(array_empty.empty(), "Incorrect array size for empty array.");
constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
你所链接的页面已经给出了第一部分的答案:
如果显式数组大小为 指定,但更短 初始化列表,则 未指定的元素被设置为零。
没有内置方法将整个数组初始化为某个非零值。
至于哪个更快,通常的规则是:“给编译器最大自由的方法可能更快”。
int array[100] = {0};
简单地告诉编译器“将这100个整数设置为零”,编译器可以自由优化。
for (int i = 0; i < 100; ++i){
array[i] = 0;
}
更加具体。它告诉编译器创建一个迭代变量i,它告诉编译器元素初始化的顺序,等等。当然,编译器很可能会优化掉这个问题,但问题的关键在于,这里您过度指定了问题,迫使编译器更加努力地工作才能得到相同的结果。
最后,如果你想将数组设置为一个非零值,你应该(至少在c++中)使用std::fill:
std::fill(array, array+100, 42); // sets every value in the array to 42
同样,你也可以用数组来做同样的事情,但是这样更简洁,并且给了编译器更多的自由。你只是说你想让整个数组充满值42。你没有说应该按什么顺序做,或者其他什么。
在c++编程语言V4中,Stroustrup建议使用vector或valarray而不是内置数组。使用valarray,当你创建它们时,你可以初始化它们为一个特定的值,比如:
valarray <int>seven7s=(7777777,7);
初始化一个包含"7777777"成员的数组。
这是一种c++实现答案的方式,使用c++数据结构而不是“普通的旧C”数组。
我转而使用valarray作为在我的代码中尝试使用c++ 'isms v. C'isms....的尝试