Является ли `std:: array <T, 0>` default конструктивным, где `T` не является конструктивным по умолчанию?
Рассмотрим приведенный ниже код:
#include <array>
struct T
{
T() = delete;
};
int main()
{
std::array<T, 0> a;
a.size();
}
Мы по умолчанию инициализируем массив размером 0. Поскольку нет элементов, конструктор T
не должен быть вызван.
Однако Clang по-прежнему требует конструкцию T
, а GCC принимает указанный выше код.
Обратите внимание, что если мы изменим инициализацию массива на:
std::array<T, 0> a{};
Clang принимает его на этот раз.
Предоставляет ли конструктор T
, не поддерживаемый по умолчанию, предотвращать конструкцию std::array<T, 0>
по умолчанию?
Ответы
Ответ 1
Благодаря @TC, как указано в comment, он адресован в LWG 2157, который по-прежнему остается открытым вопросом на момент написания этой статьи.
В предлагаемой резолюции добавляется этот пункт (выделено мной):
Неопределенная внутренняя структура массива для этого случая допускает инициализацию типа:
array<T, 0> a = { };
и указанные инициализации должны быть действительными , даже если T не является конструктивным по умолчанию.
Итак, ясно, что предполагаемое поведение должно иметь конструкцию по умолчанию std::array<T, 0>
по умолчанию, даже если T не является.
Ответ 2
Поскольку нет элементов, конструктор T не следует вызывать. Предоставляет ли конструктор T предотвращающий конструкцию T std::array<T, 0>
конструкцию по умолчанию?
В стандарте не указывается, какой макет std::array<T, 0>
должен иметь для нас ответ. Говорят, что специализация массива нулевого размера ведет себя следующим образом:
[array.zero]
1 массив должен обеспечивать поддержку для специального случая N == 0.
2 В случае, когда N == 0, begin() == end() == уникальное значение. Возвращаемое значение данных() не указано.
3 Эффект вызова front() или back() для массива нулевого размера - undefined.
4 Функция функции swap() должна иметь спецификацию исключения без метаданных.
Поведение, которое вы заметили, скорее всего связано с различиями в реализации.
Ответ 3
Этот вопрос объясняет, что происходит с clang и std::array
Удаленный конструктор по умолчанию. Объекты все еще могут быть созданы... иногда
Но с gcc
разница исходит из кода библиотеки. В gcc-базе данных gcc действительно есть конкретная деталь реализации, которая имеет отношение к этому вопросу, поскольку @StoryTeller упомянул
gcc имеет специальный случай для std::array
с размером 0, см. следующий код из заголовка <array>
(из gcc 5.4.0
)
template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
typedef _Tp _Type[_Nm];
static constexpr _Tp&
_S_ref(const _Type& __t, std::size_t __n) noexcept
{ return const_cast<_Tp&>(__t[__n]); }
static constexpr _Tp*
_S_ptr(const _Type& __t) noexcept
{ return const_cast<_Tp*>(__t); }
};
template<typename _Tp>
struct __array_traits<_Tp, 0>
{
struct _Type { };
static constexpr _Tp&
_S_ref(const _Type&, std::size_t) noexcept
{ return *static_cast<_Tp*>(nullptr); }
static constexpr _Tp*
_S_ptr(const _Type&) noexcept
{ return nullptr; }
};
как вы можете видеть, существует специализация __array_traits
(которая используется в std::array
для базового массива), когда размер массива равен 0, у которого даже нет массива типа, на котором он был установлен, Тип _Type
не является массивом, а пустой структурой!
Вот почему нет вызываемых конструкторов.