Получить размер std::array без экземпляра

Учитывая эту структуру:

struct Foo {
  std::array<int, 8> bar;
};

Как я могу получить количество элементов массива bar, если у меня нет экземпляра Foo?


person ChronoTrigger    schedule 27.12.2016    source источник
comment
как насчет использования макроса вместо жесткого кодирования размера массива?   -  person Webert Lima    schedule 27.12.2016
comment
@ WebertS.Lima - Боже мой. Не макрос.   -  person StoryTeller - Unslander Monica    schedule 27.12.2016
comment
sizeof(Foo::bar) возвращает размер в байтах вместо количества элементов. Я внесу ясность в вопрос.   -  person ChronoTrigger    schedule 27.12.2016
comment
@StoryTeller, прежде чем редактировать вопрос, я думал, что он хочет получить максимальный размер массива, а не текущий размер.   -  person Webert Lima    schedule 27.12.2016
comment
@ WebertS.Lima - а для std::array отличается?   -  person max66    schedule 27.12.2016
comment
@WebertS.Lima - std::array - это контейнер фиксированного размера. Это то же самое. Моя проблема с вашим предложением заключается не в устранении магического числа (что отлично), а в использовании для этого макроса.   -  person StoryTeller - Unslander Monica    schedule 27.12.2016
comment
@StoryTeller прав, но он хочет подсчитать, сколько элементов находится в массиве в какой-то момент. Вот что я имею в виду под текущим размером по сравнению с максимальным размером.   -  person Webert Lima    schedule 27.12.2016
comment
@WebertS.Lima - std::array не уменьшается и не растет. Всегда будет 8 построенных объектов.   -  person StoryTeller - Unslander Monica    schedule 27.12.2016
comment
Ну, есть Foo().bar.size() . Q был бы более интересным, если бы у Foo не было конструктора по умолчанию   -  person M.M    schedule 27.12.2016
comment
@StoryTeller Думаю, я неправильно понял вопрос. Извините и спасибо за разъяснение.   -  person Webert Lima    schedule 27.12.2016
comment
@WebertS.Lima - текущий размер по сравнению с максимальным размером является проблемой для контейнеров переменного размера (карт, векторов, наборов и т. д.), но std::array имеет фиксированный размер.   -  person max66    schedule 27.12.2016
comment
@ max66 Понятно, но, насколько мне известно, я думал, что вы можете объявить std:array размером 8 и поместить в него 4 элемента, не так ли? Вот о чем я думал.   -  person Webert Lima    schedule 27.12.2016
comment
@WebertS.Lima - Нет; если вы объявите std::array<int, 8>, у вас когда-нибудь будет контейнер с 8 элементами; вы можете установить значение 4 из этих элементов (после инициализации), но остальные 4 присутствуют с начальным значением   -  person max66    schedule 27.12.2016
comment
@ max66 О, понятно. Теперь я это прекрасно понимаю. Большое тебе спасибо.   -  person Webert Lima    schedule 27.12.2016
comment
@WebertS.Lima - может быть, вы думаете о std::vector<int>, где вы можете reserve() размер для 8 элементов и поместить в него только 4.   -  person max66    schedule 27.12.2016
comment
@ max66 Я неправильно истолковал, как работает std::array.   -  person Webert Lima    schedule 27.12.2016
comment
Кроме того, массив еще не существует, я очень надеюсь, что он не заполнен наполовину.   -  person Carl    schedule 28.12.2016


Ответы (6)


Вы можете использовать std::tuple_size:

std::tuple_size<decltype(Foo::bar)>::value
person Jarod42    schedule 27.12.2016
comment
Зацепим это здесь: различные классы и функции, работающие с std::tuples (std::tuple_size, std::tuple_element, std::get), имеют удобную специализацию для обработки std::array<T, N> как std::tuple<T, T, ..., T>. - person Quentin; 27.12.2016

Несмотря на хороший ответ @Jarod42, вот еще одно возможное решение, основанное на decltype, которое не использует tuple_size.
Он следует минимальному рабочему примеру, который работает в C++11:

#include<array>

struct Foo {
    std::array<int, 8> bar;
};

int main() {
    constexpr std::size_t N = decltype(Foo::bar){}.size();
    static_assert(N == 8, "!");
}

std::array уже имеет функцию-член constexpr с именем size, которая возвращает искомое значение.

person skypjack    schedule 27.12.2016
comment
так что это имеет ту же проблему, что и ответ @waxrat ниже, он требует построения, даже если эта конструкция может быть constexpr, в этом смысле std::tuple_size является лучшим ответом, потому что он не требует никакого строительства или разрушения в не-constexpr случай. - person Mgetz; 27.12.2016
comment
@Mgetz, я не говорил, что это лучше. :-) ... Различные решения и ответы могут помочь будущим читателям, независимо от того, приняты они или нет. - person skypjack; 27.12.2016
comment
следовательно, мой комментарий, я стремился гарантировать, что такие читатели поймут, почему std::tuple_size является лучшим выбором. Тем не менее, я бы, вероятно, сделал using array_size = std::tuple_size для удобства чтения. - person Mgetz; 27.12.2016
comment
Я согласен, что имена @Mgetz - не самая сильная сторона языка. Мы имеем дело с языком, который содержит функцию с именем std::move, которая ничего не перемещает. Что еще? :-) (примечание: просто шучу) - person skypjack; 27.12.2016
comment
Этот ответ имеет ту же проблему, что и другой ответ (выиграл не работает для классов без конструктора по умолчанию) Кроме того (для нетривиальных типов) он будет выполнять конструктор N раз (возможно, с побочным эффектом). - person user202729; 08.02.2020

Вы можете дать Foo public static constexpr участника.

struct Foo {
 static constexpr std::size_t bar_size = 8;
 std::array<int, bar_size> bar;
}

Теперь вы знаете размер столбца из Foo::bar_size, и у вас есть дополнительная возможность называть bar_size чем-то более описательным, если Foo когда-либо имеет несколько массивов одинакового размера.

person Willy Goat    schedule 27.12.2016
comment
Учитывая другие ответы, показывающие, что это можно сделать без static const, я не считаю это хорошим ответом. - person Tas; 28.12.2016
comment
@Tas: На мой взгляд, проблема в том, что этот ответ возвращает вещи в обратном направлении: вместо ответа на вопрос о том, как получить количество элементов некоторого существующего массива, он говорит объявить известное число и изменить объявление массива, чтобы шаблонировать его на этом . Итак, участник static constexpr на самом деле не то, на что можно жаловаться; он не будет занимать места в экземплярах и будет полностью оптимизирован в своей единице перевода (если только его адрес не будет взят, но тогда это потребует внестрочного определения). - person underscore_d; 29.12.2016
comment
@Tas @underscore_d Спасибо. Причина, по которой я сделал этот ответ, заключается в том, что ответы с высокими баллами были сложными. Размер бара без static constexpr — магическое число. Это решение удаляет магическое число и решает проблему одной простой строкой. - person Willy Goat; 31.12.2016

Вы можете сделать это так же, как и для устаревших массивов:

sizeof(Foo::bar) / sizeof(Foo::bar[0])
person Waxrat    schedule 27.12.2016
comment
или используйте гораздо более читаемый std::extent, но это не ответ, так как этот все равно потребуется построить экземпляр. - person Mgetz; 27.12.2016
comment
Следует помнить еще несколько вещей: sizeof std::array<int, 4> может не совпадать с sizeof int[4], это детали реализации. Кроме того, если вы собираетесь создать std::array, просто используйте size() член - person Mgetz; 27.12.2016
comment
Я имел в виду sizeof(Foo::bar) / sizeof(Foo::bar[0]). Не требуется экземпляр Foo. - person Waxrat; 27.12.2016
comment
@Mgetz Разве другие, явные требования std::array не означают, что в действительности он должен иметь тот же размер и макет, что и необработанный массив, даже если это явно не требуется? - person underscore_d; 27.12.2016
comment
@underscore_d на самом деле да, но это все еще зависит от реализации. Таким образом, я бы не стал этому доверять, компилятор может сделать слишком много вещей, чтобы все испортить, и std::array все равно имеет член size(). - person Mgetz; 28.12.2016

Использовать:

sizeof(Foo::bar) / sizeof(int)
person Gilson PJ    schedule 27.12.2016
comment
Я пробовал, и я получаю одинаковое значение для обоих. #include <iostream> #include <array> using namespace std; struct Foo { std::array<int, 8> bar; }; int main() { cout << sizeof(Foo::bar) / sizeof(int) << endl; cout << std::tuple_size<decltype(Foo::bar)>::value << endl; return 0; } - person Gilson PJ; 27.12.2016

Вы можете использовать как:

sizeof Foo().bar
person Happy    schedule 27.12.2016
comment
См. этот комментарий от OP: он после количества элементов , а не общий размер. - person Quentin; 27.12.2016
comment
@Happy - ОП изменил ответ: ему нужно количество элементов в массиве (8 в примере), а не sizeof - person max66; 27.12.2016
comment
примечание: не будет работать для классов без конструктора по умолчанию - person M.M; 27.12.2016
comment
@M.M Поскольку sizeof не оценивает свой операнд, вы всегда можете просто std::declval<Foo>(). - person Angew is no longer proud of SO; 27.12.2016
comment
@M.M Это работает для меня (исправленная версия с удаленным ctor по умолчанию). - person Angew is no longer proud of SO; 27.12.2016
comment
@Angew технически std::array может иметь отступы - person M.M; 27.12.2016
comment
@M.M Хорошо, но ... ой. Я не думаю, что люди были бы слишком довольны std::array реализацией, которая делала отступы. Но вы правы. - person Angew is no longer proud of SO; 27.12.2016