Передайте ссылочный аргумент конструктору элемента контейнера STL

Очень часто в C++ мне нужен контейнер STL типа, который имеет ссылочный элемент:

struct C {};

struct S
{
    S() = default;

    S(const C& c) : _c(c)
    {}

    const C& _c;   // I always have to use a pointer here
};

int main()
{
    C c;
    std::array<S, 10> arr{ c };   // Can c be passed here?

    return 0;
}

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

Вышеупомянутое не компилируется.

error: no matching constructor for initialization of 'S'

Можно ли передать ссылку через конструктор std::array и не использовать указатель?

Вас может заинтересовать std::reference_wrapper и связанная с ней вспомогательная функция std::ref.

Nathan Pierson 11.04.2024 14:58
S() = default; «неправильный» (фактически удален).
Jarod42 11.04.2024 15:05

При правильном номере инициализатора все работает Демо.

Jarod42 11.04.2024 15:06

Ссылочный член делает класс нестандартным для конструирования и теряет также некоторые другие свойства, что затрудняет использование класса.

Jarod42 11.04.2024 15:08

Почему бы не использовать член-указатель?

HolyBlackCat 11.04.2024 15:25

@Jarod42 Есть ли элегантный способ реализовать ваше решение для 1000 элементов массива?

intrigued_66 11.04.2024 15:26
std::array<S, 1000> arr{c,c,c,c,c,c,c,c,c,c,c,c,c,c, продолжайте повторять c,, чтобы заполнить все 1000 элементов — это элегантное решение.
Eljay 11.04.2024 15:32

Напишите make_array Посмотрите еще один ответ. Обратите внимание, что вам придется вызывать его с помощью S{c}.

Jarod42 11.04.2024 15:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
129
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Из приведенного примера вы можете сделать _cstatic. (Также переименуйте, чтобы не было подчеркивания, поскольку все переменные, начинающиеся с _, зарезервированы для стандартной библиотеки.)

Если нет, вы, вероятно, ищете:

 std::array<S, 10> array{S{c}};

Здесь 10 S построены из одинаковых c

Если сделать S::c статическим, то его придется инициализировать раньше main... И изменится семантика класса.

Jarod42 11.04.2024 15:11
S{c} не был нужен, S(const C&) подразумевается. Но он создаст только первый элемент, проблема связана с остальными 9 объектами, которые не могут быть инициализированы по умолчанию.
Jarod42 11.04.2024 15:12

Изменение c на static — это изменение дизайна. В вопросе нет ничего, что указывало бы на то, что это уместно.

Pete Becker 11.04.2024 15:25

Также переименуйте, чтобы не было подчеркивания, поскольку все переменные, начинающиеся с _, зарезервированы для стандартной библиотеки. это неверно. Это верно только в глобальном масштабе. В любой области действия, начиная с _, за которым следуют прописные буквы, зарезервировано; _, за которым следует строчная буква, не зарезервирована и может использоваться свободно (за пределами глобальной области видимости). Кроме того, двойное подчеркивание __ в любом месте идентификатора также зарезервировано в любой области действия.

Eljay 11.04.2024 15:25

Нет, имена, начинающиеся с подчеркивания, не зарезервированы для стандартной библиотеки. Имена, начинающиеся с подчеркивания, за которым следует заглавная буква, зарезервированы для использования реализацией. Имена, начинающиеся с подчеркивания, за которым следует строчная буква, зарезервированы для использования реализацией в глобальной области видимости. Нет ничего плохого (кроме того, что <g> выглядит глупо) в их использовании в области блока.

Pete Becker 11.04.2024 15:27

В вашем коде есть 2 проблемы:

  1. Вы не можете сделать конструктор по умолчанию дефолтным с помощью:

    S() = default;
    

    Потому что ссылка на член _c должна быть инициализирована.

  2. arr следует инициализировать 10 элементами (поскольку S нельзя сконструировать по умолчанию, как описано выше):

    std::array<S, 10> arr{c, c, c, c, c, c, c, c, c, c};
    

Демо

Ответ принят как подходящий

Можно ли передать ссылку через конструктор std::array и не использовать указатель?

[...] продолжайте повторять c, как заполнить все элементы 1000 — это элегантное решение?

Если вам не нравятся указатели, я бы предложил использовать std::reference_wrapper вместо ссылок на плоскости. Почему:

#include <functional> // std::reference_wrapper

struct S
{
    constexpr explicit S(C& c) 
        : _c{c} {}

private:
    std::reference_wrapper<C> _c;
};

Теперь, чтобы заполнить массив (в C++20), вы можете написать помощник make_ref_array() следующим образом:

template <size_t N>
constexpr auto make_ref_array(C& refArg)
{
    return [&refArg]<size_t... Is>(std::index_sequence<Is...>) {
        return std::array<S, sizeof...(Is)>{(static_cast<void>(Is), S{ refArg })...};
    }(std::make_index_sequence<N>{});
}

так что создание массива будет похоже на

C c;
auto arr = make_ref_array<100u>(c);

Посмотрите живую демонстрацию

Теперь это может быть более общим, например:

template <typename T, size_t N, typename... Args>
constexpr auto make_ref_array(Args&... args)
{
    return []<size_t... Is, typename... Ts>(std::index_sequence<Is...>, Ts&... ts) {
        return std::array<T, sizeof...(Is)>{(static_cast<void>(Is), T{ ts... })...};
    }(std::make_index_sequence<N>{}, args...);
}

Обратите внимание, что для построения args будут использоваться все T, даже если другие элементы данных не являются ссылками.


Для компиляторов старше C++20 лямбда-выражение шаблона необходимо переместить в отдельную функцию шаблона, как , как в этом примере

Спасибо тебе за это. Я просто..... озадачен, почему C++ не может сделать это «под капотом». Когда кто-то пишет std::array<S, 1000> arr{c};, он явно хочет передать всем 1000 элементам! Никто не инициализирует только один элемент массива.

intrigued_66 22.04.2024 04:42

Другие вопросы по теме