Чому std :: array не має конструктора, який приймає значення для масиву, яким потрібно заповнити?


77

Це відсутність

std::array<T,size>::array(const T& value);

недогляд? Мені це здається дуже корисним, і динамічні контейнери (як std::vector) мають подібний конструктор.

Я цілком усвідомлюю це

std::array<T,size>::fill(const T& value);

але це не конструктор, і пам'ять спочатку обнуляється. Що робити, якщо я хочу, щоб все було -1як цей хлопець ?


1
"і пам’ять спочатку буде обнулена"? Ви впевнені, що це правда?
tohava

3
Він не буде обнулений спочатку, якщо ви про це не попросите.
R. Martinho Fernandes

1
Окрім сукупного аргументу з усіх відповідей, можуть бути й більш концептуальні міркування. Конструктор заливки, мабуть, приховував би той факт, що насправді не будує окремих елементів. Він насамперед викличе сукупну ініціалізацію, а потім скопіює значення в елементи, він не може відразу скопіювати-побудувати елементи (на відміну від, скажімо, а std::vector). Отож, оскільки це завжди було б еквівалентно array(); array.fill();, пропуск конструктора в першу чергу не приховує цього факту.
Крістіан Рау,

1
Також актуальні: stackoverflow.com/questions/18497122 / ...
kennytm

Відповіді:


54

std::array за задумом є сукупністю, тому не має оголошених користувачем конструкторів.

Як ви говорите, ви можете використовувати його fillпісля побудови за замовчуванням. Оскільки це сукупність, конструкція за замовчуванням не обнуляє пам’ять, а залишає її неініціалізованою (якщо вміщений тип тривіально ініціалізується).


1
Тож ця сторінка помилкова? Там прямо сказано, що елементи масиву ініціалізовані за замовчуванням.
rubenvb

15
Ініціалізація за замовчуванням - це не ініція для POD, а конструктор за замовчуванням - для всього іншого, що я вважаю - залежно від точки оголошення.
Xeo

1
@rubenvb: Так, вони будуть ініціалізовані за замовчуванням, а не ініціалізовані. Отже, якщо вони тривіально ініціалізуються, то вони залишаться неініціалізованими.
Майк Сеймур,

Ах так. Це все ще залишається дуже пустотливою частиною стандартного IMO, яке проводить різницю між користувальницьким та вбудованим типами:/
rubenvb

11
@rubenvb Усі примітивні типи мають тривіальну ініціалізацію за замовчуванням. Визначені користувачем типи можуть поводитися однаково при бажанні. Це не різниця, це послідовність.
Кейсі

23

Зверніть увагу, що ви можете ефективно моделювати цей тип конструктора, скориставшись тим, що масив не ініціалізований нулем, а має конструктор копіювання і do.

template <size_t N, class T>
array<T,N> make_array(const T &v) {
    array<T,N> ret;
    ret.fill(v);
    return ret;
}

auto a = make_array<20>('z');

давайте не будемо роздувати сайт викликів непотрібними параметрами шаблону ;-).
rubenvb

4
charможна зробити висновок, тож ви можете просто писати make_array<20>('z') замістьmake_array<20,char>('z')
Nawaz

@Nawaz о, ще краще. Я повинен був запитати, чому make_arrayзамість цього немає:-)
rubenvb

1
@Walter: Ви не можете повернути будь-яке посилання, оскільки повернете посилання на локальну змінну.
GManNickG 02

3
Це не буде працювати, якщо Tце неможливо побудувати за замовчуванням, і саме тоді вам дуже потрібен конструктор заливки.
Кріс Бек,

12

Ви можете використовувати std::index sequenceдля цього:

namespace detail
{

    template <typename T, std::size_t...Is>
    constexpr std::array<T, sizeof...(Is)>
    make_array(const T& value, std::index_sequence<Is...>)
    {
        return {{(static_cast<void>(Is), value)...}};
    }
}

template <std::size_t N, typename T>
constexpr std::array<T, N> make_array(const T& value)
{
    return detail::make_array(value, std::make_index_sequence<N>());
}

Демо

std::make_index_sequence є C ++ 14, але може бути реалізований в C ++ 11.

static_cast<void>(Is)полягає у боротьбі зі злом, operator,яке Tможе забезпечити.


3
Це найкорисніша відповідь, вона працює також, коли Tнеможливо побудувати за замовчуванням, тобто тоді, коли інші відповіді не вистачають.
Кріс Бек,

1
@ChrisBeck Не можу більше погодитись. Це було саме для мене, і я був розчарований іншими відповідями. Має на меті показати, що підняті голоси - це не все.
Jonas Greitemann 02

Через використання рекурсії шаблону це не компілюється для великих масивів. Якщо мені потрібен масив з мільйона елементів T без конструктора за замовчуванням, мені не пощастило? Навіть масив C працював би для мене.
Міхал Бжозовський,

@ MichałBrzozowski: std::index_sequenceможе бути реалізовано з використанням логарифму, а не лінійним. і компілятор може мати "intrinsics", щоб зробити лише один екземпляр. Потім рекурсія більше не існує.
Jarod42,

1
@ Міхал Бржозовський: Ви все ще можете переключитися на std::vector, і reserveцілий розмір, і emplace_backв циклі. Що стосується мільйона елементів, то стек буде проблематичним, оскільки пам’ять на практиці обмежена.
Jarod42,

10

Перш за все, це не так std::array<T>, саме std::array<T,N>там Nвідбувається компіляція постійного інтегрального виразу часу.

По-друге, std::arrayробиться агрегатним за проектом. Отже, у нього немає нічого, що робить його неагрегатним, саме тому у нього немає конструктора ... та деструктора, віртуальних функцій тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.