Code Review StackExchangeの学生のコードや、他の人のかなりの数の教材(!)でさえよく見かける、アンチパターンに注意を向けたいと思います。たとえば、5つの要素の配列があります。そして、魔法の数が悪いので、「5」のカーディナリティを表す名前付き定数を導入します。
void example()
{
constexpr int myArraySize = 5;
int myArray[myArraySize] = {2, 7, 1, 8, 2};
...
しかし、解決策はまあまあです!上記のコードでは、5番目の数字が繰り返されています。最初は値
myArraySize = 5
で、次に実際に要素を割り当てるときにもう一度繰り返されますmyArray
。上記のコードは、メンテナンスの観点からは次のようにひどいものです。
constexpr int messageLength = 45;
const char message[messageLength] =
"Invalid input. Please enter a valid number.\n";
-もちろん、私たちの誰もこれを書くことはありません。
繰り返されるコードは良くありません
上記の両方のコードスニペットで、配列の内容またはメッセージの文言を変更するたびに、1行ではなく2行のコードを更新する必要があることに注意してください。メンテナがこのコードを誤って更新する方法の例を次に示します。
constexpr int myArraySize = 5;
- int myArray[myArraySize] = {2, 7, 1, 8, 2};
+ int myArray[myArraySize] = {3, 1, 4};
上記のパッチは、配列の内容を2,7,1,8,2から3,1,4に変更しているように見えますが、そうではありません。実際に、彼はそれを変更3,1,4,0,0 -ゼロパディングと-メンテナを調整するために忘れてしまったので、によると。
myArraySize
myArray
信頼できるアプローチ
カウントに関しては、コンピューターはかなり上手です。だから、コンピュータを数えましょう!
int myArray[] = {2, 7, 1, 8, 2};
constexpr int myArraySize = std::size(myArray);
これで、1行のコードを変更するだけで、配列の内容をたとえば2,7,1,8,2から3,1,4に変更できます。変更をどこにでも複製する必要はありません。
さらに、
myArray
実際のコードは通常for
、反復子の範囲に基づくループやアルゴリズムを使用して操作するため、配列のサイズを格納するために名前付き変数はまったく必要ありません。
for (int elt : myArray) {
use(elt);
}
std::sort(myArray.begin(), myArray.end());
std::ranges::sort(myArray);
// Warning: Unused variable 'myArraySize'
このコードの「悪い」バージョンは
myArraySize
常に(宣言でmyArray
)使用されるため、プログラマーはそれが除外できることに気付かない可能性があります。「良い」バージョンでは、コンパイラはmyArraySize
使用されていないものを簡単に検出できます。
これをどのように行うのstd::array
ですか?
プログラマーがダークサイドに向けて別の一歩を踏み出し、次のように書くことがあります。
constexpr int myArraySize = 5;
std::array<int, myArraySize> myArray = {2, 7, 1, 8, 2};
これは、少なくとも次のように書き直す必要があります。
std::array<int, 5> myArray = {2, 7, 1, 8, 2};
constexpr int myArraySize = myArray.size(); // std::size(myArray)
ただし、最初の行の手動カウントを取り除く簡単な方法はありません。CTAD C ++ 17では次のように書くことができます
std::array myArray = {2, 7, 1, 8, 2};
ただし、これは配列が必要な場合にのみ機能します。たとえば、
int
配列short
や配列が必要な場合は機能しませんuint32_t
。
C ++ 20は、std :: to_arrayを提供します。これにより、次のように記述できます。
auto myArray = std::to_array<int>({2, 7, 1, 8, 2});
constexpr int myArraySize = myArray.size();
これによりC配列が作成され、その要素がに移動(move-construct)されることに注意してください
std::array
。これまでのすべての例はmyArray
、集約の初期化をトリガーし、アレイ要素を所定の位置にインスタンス化する中括弧付きの初期化子リストで初期化されました。
いずれにせよ、これらのオプションはすべて、古き良きCアレイ(テンプレートのインスタンス化を必要としない)と比較して、多数の追加のテンプレートインスタンスをもたらします。したがって、私
T[]
は新しいものを強く好みstd::array<
T, N>
ます。
C ++ 11とC ++ 14では、
std::array
言うことができるという人間工学的な利点がありましたarr.size()
。しかし、C ++ 17が私たちに提供したとき、その利点は蒸発しましたstd::size(arr)
インラインアレイの場合。std::array
人間工学的な利点はもうありません。オブジェクト全体の変数セマンティクスが必要な場合に使用します(配列全体を関数に渡します!関数から配列を返します!=で配列を割り当てます!==!で配列を比較します)が、それ以外の場合は、の使用を避けることをお勧めしstd::array
ます。
同様に、std::list
イテレーターの安定性、高速接着、要素を置き換えずに並べ替えるなどが必要な場合を除いて、を避けることをお勧めします。C++にこれらのタイプの場所がないと言っているわけではありません。私は彼らが「非常に特定のスキルのセット」を持っていると言っているだけです、そしてあなたがそれらのスキルを使わなければ、あなたはおそらく過払いです。
結論:馬の前でカートを囲わないでください。実際、カートは必要ないかもしれません。また、ゼブラを使用して馬の仕事をする必要がある場合は、ゼブラの前でカートをフェンスで囲うべきではありません。
続きを読む: