Неоднородный список инициализаторов

У меня есть вспомогательные классы QueryField и Select, используемые для построения операторов SQL:

class QueryField
{
public:
    QueryField(std::string_view column)
        : m_column{ column }
    {
    }
    QueryField(std::string_view column, std::string_view alias)
        : m_column{ column }
        , m_alias{ alias }
    {
    }

private:
    std::string m_column;
    std::string m_alias;
};

class Select
{
public:

    Select(std::initializer_list<QueryField> fields)
    {
        for (auto & field : fields)
        {
            m_fields.emplace_back(std::move(field));
        }
    }

private:

    std::vector<QueryField> m_fields;
};

как видно из приведенного выше кода, Select представляет собой набор объектов QueryField, которые можно инициализировать следующим образом:

Select{ QueryField{ "up.audit_option" "option" }, QueryField("uep.success"), QueryField("uep.failure") };

можно ли исключить необходимость явного указания QueryField и инициализировать объект Select следующим образом?

Select{ { "up.audit_option" "option" }, "uep.success", "uep.failure" };

Я пытался сделать это один раз и даже спросил на ТАК, я думаю. Я почти уверен, что гетерогенные списки инициализации в настоящее время невозможны в cpp.

bartop 27.05.2019 16:40

поправьте меня, если я ошибаюсь, но вам нужен однородный (одинаковые типы), а не разнородный (разные типы) список

local-ninja 27.05.2019 16:50

Какой компилятор вы используете? Мне кажется, компилируется нормально? godbolt.org/z/XwgbD-? Или я что-то упускаю?

divinas 27.05.2019 16:54
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
373
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

С вашим решением вы действительно можете отказаться от типов, но вы должны сохранить фигурные скобки:

Select{ { "up.audit_option" "option" }, {"uep.success"}, {"uep.failure"} }

Также будьте осторожны с инициализированным списком: все элементы внутри будут скопированы. Даже если вы переедете:

Select(std::initializer_list<QueryField> fields)
{
    for (auto & field : fields)
    {
        // Actually copy. No move is done.
        m_fields.emplace_back(std::move(field));
    }
}

Никакое перемещение не разрешено, так как все элементы в списке инициализаторов являются постоянными.


Моим предпочтительным решением было бы отказаться от std::initializer_list и быть простым с простым случаем и более явным со сложными случаями.

Чтобы разрешить настоящие разнородные параметры, я буду использовать вариативные шаблоны:

template<typename... Args>
Select(Args&&... fields) :
    m_fields{QueryField{std::forward<Args>(args)}...} {}

Если вы хотите сохранить конструктор копирования/перемещения, вы должны отфильтровать некоторые типы параметров:

template<typename T, typename = void typename... Args>
struct is_not_copy_impl : std::false_type {};

template<typename T, typename Arg>
struct is_not_copy_impl<T, std::enable_if_t<std::is_base_of_v<T, std::decay_t<Arg>>>, Arg> : std::true_type {};

template<typename T, typename... Args>
using is_not_copy = is_not_copy_impl<T, void, Args...>;

template<typename... Args, std::enable_if_t<!is_not_copy<Select, Args...>::value>* = nullptr>
Select(Args&&... fields) :
    m_fields{QueryField{std::forward<Args>(args)}...} {}

Этот код будет перемещаться при передаче QueryField и создавать новый при передаче значения другого типа.

Использование таково:

Select{
    QueryField{"up.audit_option" "option"},
    "uep.success",
    "uep.failure"
};

ИМХО конструктор с вариативным шаблоном, вероятно, лучше, но он скрывает конструкторы копирования и перемещения. Выберите select(other) и Select select(std::move(other)) останавливает компиляцию. Я объявил оба конструктора по умолчанию, но это не помогает.

Alexey Starinsky 28.05.2019 16:25

Возможно, решение состоит в том, чтобы сделать его не конструктором, а функцией типа MakeSelect(Args &&... fields) (например, std::make_shared)

Alexey Starinsky 28.05.2019 16:57

Ах да, вы должны отфильтровать конструктор перемещения и копирования. я отредактировал ответ

Guillaume Racicot 28.05.2019 17:25

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