инициализация std::array с nullptr без параметров шаблона

Вопрос или проблема

Я использую API, который ожидает непрерывный блок памяти, содержащий указатели. Указатели могут быть nullptr.

В данный момент я использую массивы C:

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));

Мой инструментальный комплекс содержит clang-tidy, который советует избегать массивов C и предпочитать std::array.

std::array имеет руководящую схему вывода, позволяющую инициализировать такой массив следующим образом:

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
};

Это работает только в том случае, если первый элемент и все последующие элементы могут быть неявно преобразованы к одному и тому же (указательному) типу, в этом случае ID3D11ShaderResourceView*.

Это нарушается, как только первый элемент содержит nullptr:

std::array srvs = {
    nullptr,
    lightmap_srv.Get(), // ошибка: "значение типа "ID3D11SamplerState *" не может быть использовано для инициализации сущности типа "std::_Enforce_same<std::nullptr_t, std::nullptr_t, ID3D11SamplerState *, std::nullptr_t, std::nullptr_t, ID3D11SamplerState *>::type" (также известный как "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
};

Это имеет смысл, потому что первый элемент имеет тип std::nullptr_t.

Могу ли я инициализировать/вывести std::array без явного указания типа и позволить первому элементу быть nullptr? Я мог бы привести первый (nullptr) элемент, но тогда я мог бы просто указать его в типе также.

Ответ или решение

Инициализация std::array с использованием nullptr без указания параметров шаблона является распространенной задачей при работе с API, требующими непрерывный блок памяти, содержащий указатели. Многие разработчики сталкиваются с этой проблемой, когда им необходимо передать массив указателей, среди которых могут встречаться и nullptr. В данной статье мы подробно рассмотрим, как правильно инициализировать std::array с учетом всех особенностей, включая использование nullptr.

Введение в std::array

std::array — это контейнер из стандартной библиотеки C++, который обеспечивает статический массив фиксированного размера. Одним из главных преимуществ использования std::array вместо C-массивов является более безопасная и удобная семантика, включающая встроенные функции для получения размера массива и доступ к элементам.

Проблема с стандартной инициализацией

Как вы уже отметили, инициализация std::array с первым элементом, равным nullptr, может привести к проблемам с выводом типа. Это связано с тем, что при инициализации компилятор пытается вывести тип, основываясь на первом элементе. В случае, когда первый элемент — это nullptr, компилятор интерпретирует его как std::nullptr_t, что вызывает затруднения при дальнейшем добавлении элементов другого типа, например, ID3D11ShaderResourceView*.

Решение проблемы

Одним из универсальных способов решения данной проблемы является указание типа при создании экземпляра std::array. Вы можете сделать это следующим образом:

#include <array>
#include <cstddef>
#include <d3d11.h>

// Предполагая, что Get() возвращает указатели на нужные ресурсы
ID3D11ShaderResourceView* room_diffuse_srv;
ID3D11ShaderResourceView* lightmap_srv;
ID3D11ShaderResourceView* room_normal_srv;

std::array<ID3D11ShaderResourceView*, 15> srvs = {
    nullptr,
    lightmap_srv,
    room_normal_srv,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};

// Передача массива в контекст
ctx->VSSetShaderResources(0, UINT(srvs.size()), srvs.data());

Дополнительные советы

  1. Использование std::nullptr_t: Если вы хотите сохранить гибкость и избежать явного указания на тип, можно использовать статическую инициализацию, но при этом нужно быть готовым к подключениям и конверсиям.

  2. Проверка указателей: Всегда полезно проверять, не является ли указатель nullptr перед его использованием в функции или методе, чтобы избежать исключений и непредвиденных ошибок.

  3. Совместимость с API: Убедитесь, что ваш массив соответствует требованиям API относительно размера и типа указателей, чтобы избежать проблем с выводом типов.

Заключение

Инициализация std::array с nullptr без указания типов шаблона может показаться сложной задачей, однако, придерживаясь вышеописанных рекомендаций, вы сможете успешно создать массив указателей, включая nullptr, без необходимости изменения логики вашего кода. Это позволит вам улучшить читаемость и безопасность вашего кода, следуя современным стандартам C++.

Оцените материал
Добавить комментарий

Капча загружается...