web-dev-qa-db-ja.com

1つのデフォルト値を持つ通常の配列の初期化

C++に関するメモ:配列の初期化 には、配列の初期化に関する素晴らしいリストがあります。私は

int array[100] = {-1};

-1でいっぱいになることを期待していますが、そうではありません、最初の値だけがあり、残りは0と乱数が混在しています。

コード

int array[100] = {0};

うまく働き、各要素を0に設定します。

私がここで欠けているものは何ですか..値がゼロでないならば、それを初期化することができませんか?

2:デフォルトの初期化(上記のように)は、配列全体を通した通常のループよりも速く、値を割り当てるのですか、それとも同じことを行いますか?

222
Milan

使用した構文を使用して、

int array[100] = {-1};

省略されたすべての要素が-1に設定されるため、「最初の要素を0に設定し、残りを0に設定する」と表示されます。

C++では、それらすべてを-1に設定するには、 std::fill_n のように使用できます(<algorithm>から)。

std::fill_n(array, 100, -1);

ポータブルCでは、あなたはあなた自身のループをロールバックしなければなりません。コンパイラ拡張機能がありますが、それが許容できる場合はショートカットとして実装定義の動作に依存することもできます。

322
Evan Teran

構文を許可するgccコンパイラへの拡張があります。

int array[100] = { [0 ... 99] = -1 };

これにより、すべての要素が-1に設定されます。

これは "指定イニシャライザ"として知られています。詳細はこちらをご覧ください。

これはgcc c ++コンパイラには実装されていないことに注意してください。

127
Callum

あなたがリンクしたページはすでに最初の部分に答えを与えました:

明示的な配列サイズが指定されているが、より短い初期化リストが指定されている場合、指定されていない要素はゼロに設定されます。

配列全体をゼロ以外の値に初期化するための組み込みの方法はありません。

どちらが速いかについては、通常の規則が適用されます。「コンパイラに最も自由度を与える方法はおそらく速いです」。

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の値で埋めたいと言っているだけです。どの順序で実行するのか、それ以外のことは何も言いません。

31
jalf

C++ 11にはもう1つの(不完全な)オプションがあります。

std::array<int, 100> a;
a.fill(-1);
10
Timmmm

{}を使用して、宣言されているとおりに要素を割り当てます。残りは0で初期化されます。

初期化する= {}がない場合、内容は未定義です。

9
0x6adb015

リンクしたページの状態

明示的な配列サイズが指定されているが、より短い初期化リストが指定されている場合、指定されていない要素はゼロに設定されます。

速度の問題:これほど小さい配列では、違いはほとんどありません。大規模な配列を使用していて、サイズよりも速度がはるかに重要な場合は、デフォルト値のconst配列(コンパイル時に初期化)を指定してから、それらを変更可能な配列にmemcpyname__します。

8
laalto

配列を共通の値に初期化するもう1つの方法は、一連の定義で要素のリストを実際に生成することです。

#define DUP1( X ) ( X )
#define DUP2( X ) DUP1( X ), ( X )
#define DUP3( X ) DUP2( X ), ( X )
#define DUP4( X ) DUP3( X ), ( X )
#define DUP5( X ) DUP4( X ), ( X )
.
.
#define DUP100( X ) DUP99( X ), ( X )

#define DUPx( X, N ) DUP##N( X )
#define DUP( X, N ) DUPx( X, N )

配列を共通の値に初期化するのは簡単です。

#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };

注:DUPへのパラメーターでマクロ置換を有効にするためにDUPxが導入されました

4
Steen

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));
}

3番目の関数は主に便宜上のものです。そのため、ユーザーは自分でstd::integral_constant<std::size_t, size>を作成する必要はありません。実際の作業は、最初の2つの機能のうちの1つによって行われます。

最初のオーバーロードはかなり簡単です。サイズ0のstd::arrayを作成します。コピーする必要はありません。作成するだけです。

2番目のオーバーロードは少しトリッキーです。ソースとして取得した値に沿って転送し、また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

これは、渡された値をコピーすることによって最初のサイズ-1の引数を作成します。ここでは、可変長パラメータパックインデックスを展開するものとして使用します。そのパックにはsize - 1個のエントリがあり(make_index_sequenceの構造で指定したとおり)、それらの値は0、1、2、3、...、size - 2です。ただし、値は気にしません。 (コンパイラの警告を黙らせるために、voidにキャストしています)。パラメータパックを展開すると、コードが次のように展開されます(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の結果を無視してから配列にコピーされた値を返します。 。

最後の引数、私たちがstd::forwardを呼び出すものは、マイナーな最適化です。誰かが一時的なstd :: stringを渡して「これらのうちの5つの配列を作る」と言ったら、5コピーではなく4コピーと1移動をしたいのです。 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.");
}
3
David Stone

シングルバイト要素の配列の場合は、memsetを使用してすべての要素を同じ値に設定できます。

ここに例 があります

2
Steve Melnikoff

C++では、メタプログラミングと可変数テンプレートを使用することもできます。次の記事はその方法を示しています。 プログラム的にC++でコンパイル時に静的配列を作成する

1
ingomueller.net

1)初期化子を使うとき、そのような構造体または配列に対して、指定されていない値は基本的にデフォルトで構築されます。整数のようなプリミティブ型の場合、それはそれらがゼロにされることを意味します。これは再帰的に適用されることに注意してください。配列を含む構造体の配列を持つことができ、最初の構造体の最初のフィールドだけを指定すると、残りはすべてゼロとデフォルトコンストラクタで初期化されます。

2)コンパイラは、少なくとも手作業でできるほど良い初期化コードを生成するでしょう。可能であれば、コンパイラーに初期化を行わせることを好む傾向があります。

1
Boojum

C++プログラミング言語V4では、Stroustrupは組み込み配列よりもベクトルまたはvalarrayを使用することを推奨します。 valarraryの場合は、それらを作成するときに、次のように特定の値に初期化できます。

valarray <int>seven7s=(7777777,7);

"7777777"で長さ7のメンバーを配列の初期化します。

これは、 "plain old C"配列の代わりにC++データ構造を使用して回答を実装するC++の方法です。

私は自分のコードでC++のisms v。C'ismを使おうとする試みとしてvalarrayの使用に切り替えました。

0
Astara