前回は最終要素のひとつ先のアドレスを取得しました。今回は配列の最終要素のアドレスを取得してみます。
この場合も、元ネタにあるようなマクロを使えば求めることができます。
0 1 2 |
#define ArrayLast(array) ((array) + (ArraySizeOf(array) - (size_t)1)) |
ひとつだけ注意しないといけないのは、配列の要素数がゼロの場合です。要素数がゼロの配列というのは本当は存在しないのですが、GCCなどでは普通に作れてしまいます。サイズがゼロの場合に上記のマクロを使うと未定義の動作になってしまいますので、先に要素数が1以上であることをチェックしておいた方がいいでしょう。
そういったことも盛り込んだ形で、次のような関数を作るのもよいでしょう。
0 1 2 3 4 5 6 |
template <typename T, std::size_t N> T* array_last(T (&array)[N]) { return array + N - 1; } |
要素数のチェックはしていませんが、これだけで要素数ゼロの配列を渡そうとするとコンパイルエラーになるようです(GCCおよびClangで確認しました)。もちろん、C++11以降であればconstexprにすることも可能です。
C++11以降であれば、std::end関数(またはstd::cend関数)とstd::prev関数を組み合わせて実現することもできます。
0 1 2 3 4 5 6 |
template <typename T, std::size_t N> T* array_last(T (&array)[N]) { return std::prev(std::end(array)); } |
std::end関数とstd::prev関数であればstd::vectorなどのコンテナにも使うことができます。ただ、配列の場合は上記でかまいませんが、コンテナの場合は要素がゼロでないことを別途チェックする必要がありそうです。
C++17以降であれば、std::size関数またはstd;;empty関数が使えますので、次のように書くことができます。
0 1 2 3 4 5 6 7 8 9 |
template <typename C> auto array_last(C& c) { if (std::empty(c)) // if (std::size(c) < 1)でも可 throw std::invalid_argument(__func__); return std::prev(std::end(c)); } |
返却値の型も、まともに書くとdecltypeを使って、たとえばdecltype(std::end(c))のように書く必要がありますが、ここは簡単にautoだけにしています。