std::extent にはやや意外な挙動が含まれているので、その注意点と回避方法について説明します
std::extent は配列のサイズを求める良い手段ですが、指定した型が非配列である時には、std::extent<>::value の値が 0 になるという動作になります。
以下の例は、std::extent の引数に誤ってポインタを渡してしまう例です。ポインタは配列ではないので、std::extent<>::value の値は 0 になります。
#include <iostream> #include <type_traits> int main(void) { int a[3] = {1, 2, 3}; int* p = a // std::enxtentを使用 // std::extent<decltype(p)>::valueは 0 である for (size_t i = 0; i < std::extent<decltype(p)>::value; i++) { std::cout << a[i] << " "; // => for文に入らない } // 参考までに、C言語以来のクラシカルなやり方 // (sizeof(p) / sizeof(p[0]))は環境によって 2 などになる // 条件によっては不定動作につながってしまう for (size_t i = 0; i < (sizeof(p) / sizeof(p[0])); i++) { std::cout << a[i] << " "; // => "1 2 " } return 0; }上の例ではfor文の中に入らないので不定動作とはなりませんが、ここは p が配列でないことをコンパイルエラーで知らせてくれる方がありがたいです。
テンプレート引数が配列でない場合にはコンパイルエラーを起こす std::extent のラッパークラスの実装例を、以下に記します。
#include <iostream> #include <type_traits> // std::extentのラッパークラス // テンプレート引数が配列でなければ、static_assertが起こる template <typename ARRAY> struct array_size : std::extent<ARRAY> { static_assert(std::is_array<ARRAY>::value, "argument of array_size must be array"); }; int main(void) { int a[3] = {1, 2, 3}; int *p = a; // 配列に対して使用。std::enxtentと同じ動作 for (size_t i = 0; i < array_size<decltype(a)>::value; i++) { std::cout << a[i] << " "; // => "1 2 3 " } // ポインタに対して使用。コンパイルエラー! for (size_t i = 0; i < array_size<decltype(p)>::value; i++) { std::cout << a[i] << " "; } return 0; }
0 件のコメント:
コメントを投稿