c++arraysstdarray

initializing std::array with nullptr without template parameters


I am using an API that expects a contiguous block of memory that contains pointers. the pointers themselves can be nullptr.

Currently, I use C-Arrays:

ID3D11ShaderResourceView* srvs[] = {
    room_diffuse_srv.Get(),
    lightmap_srv.Get(),
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};
ctx->VSSetShaderResources(0, UINT(std::size(srvs)), std::data(srvs));

My toolchain contains clang-tidy, which advises to avoid C arrays and prefer std::array instead.

std::array has a deduction guide, that lets me initialize such an array like this:

std::array srvs = {
    room_diffuse_srv.Get(),
    lightmap_srv.Get(),
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};

This only works when the first element and all following elements are implicitly convertible to the same (pointer) type, in this case ID3D11ShaderResourceView*.

It breaks apart, as soon as the first element contains a nullptr:

std::array srvs = {
    nullptr,
    lightmap_srv.Get(), // error: "a value of type "ID3D11SamplerState *" cannot be used to initialize an entity of type "std::_Enforce_same<std::nullptr_t, std::nullptr_t, ID3D11SamplerState *, std::nullptr_t, std::nullptr_t, ID3D11SamplerState *>::type" (aka "std::nullptr_t")"
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};

makes sense, because the first element has the type std::nullptr_t.

Can I initialize/deduce an std::array without specifying the type explicitly and allow the first element to be nullptr? I could cast the first (nullptr) element, but then I could just write it in the type as well.


Solution

  • I think you'll need to create a helper function that can deduce the type for you:

    #include <array>
    #include <type_traits>
    #include <utility>
    
    template<typename... T>
    constexpr auto make_array(T&&... args)
        -> std::array<std::common_type_t<T...>, sizeof...(T)>
    {
        return { std::forward<T>(args)..., };
    }
    

    Then we can write:

    auto srvs = make_array(
        nullptr,
        lightmap_srv.Get(),
        room_normal_srv.Get(),
        nullptr,
        nullptr,
        nullptr,
        nullptr,
        nullptr,
        nullptr,
        input.ambient_occlusion,
        reflection_srv.Get(),
        reflection_extents_srv.Get(),
        nullptr,
        nullptr,
        nullptr
    );