Compile time size of data member std::array in non-class template (C++14)

Viewed 255

Compile-time inspection std::array data member for its (compile time) size

I need to statically assert that the compile-time size of a non-constexpr data member of type std::array, say arr_, of a non-template class is equal to a given (externally provided) constant. The static assertion will be done from inside the class, meaning arr_ is accessible, but I cannot rely on any stored constant (nor a non-type template parameter) for its size. I.e., the assertion needs to rely solely on "some inspection" of the arr_ data member.

I would basically be done if the constexpr std::array<>::size()/std::array<>::max_size() was static member functions (decltype(arr_)::size()/decltype(arr_)::max_size()) instead of a non-static member functions.

I have a working approach using function template argument deduction on a pointer-to-data-member for the arr_ member, but I'm wondering if there is an easier/neater approach.

#include <array>
#include <cstddef>

// Defined/provided from elsewhere.
constexpr std::size_t kArraySize = 12U;
constexpr std::size_t kAnotherArraySize = 12U;

template <typename T, typename U, std::size_t N>
constexpr std::size_t size_of_data_member_array(std::array<T, N> U::*) {
    return N;
}

class Foo {
    std::array<int, kArraySize> arr_;
    static_assert(size_of_data_member_array(&Foo::arr_) == kAnotherArraySize, "");
};

int main() {}
4 Answers

The standard provides a static version of array::size under the name tuple_size:

#include <array>
#include <tuple> // for std::tuple_size_v

static_assert(std::tuple_size<decltype(arr_)>::value == kAnotherArraySize, "");
static_assert(std::tuple_size_v<decltype(arr_)> == kAnotherArraySize); // C++17

You can create an instance of an array with the same type of Foo::arr_ within the static assertion:

class Foo {
    std::array<int, kArraySize> arr_;
    static_assert(decltype(arr_){}.size() == kAnotherArraySize, "");
};

See this example.

Note: this works only if the array value type is a POD or has a default constexpr constructor.

Just to offer another option, you can do partial specialization on template variables.

#include <array>
#include <cstddef>

// Defined/provided from elsewhere.
constexpr std::size_t kArraySize = 12U;
constexpr std::size_t kAnotherArraySize = 12U;

template <typename T>
constexpr std::size_t array_size = 0;

template <typename T, std::size_t N>
constexpr std::size_t array_size<std::array<T, N>> = N;

class Foo {
    std::array<int, kArraySize> arr_;
    static_assert(array_size<decltype(arr_)> == kAnotherArraySize, "");
};

int main() {}

To offer yet another option using SFINAE:

#include <type_traits>
#include <array>

template <typename T>
constexpr bool is_array_v = false;

template <typename T, size_t N>
constexpr bool is_array_v<std::array<T, N>> = true;

template <typename Array, std::enable_if_t<is_array_v<Array>, int> = 0>
constexpr size_t array_size_v = std::tuple_size<Array>::value;

static_assert(array_size_v<std::array<int, 5>> == 5); // OK
static_assert(array_size_v<int> == 5);                // Won't compile

Unlike the other proposals, this will catch misuse of array_size_v at compile time, so entering an int and other types which are not std::array won't work.

Related