Чому 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.