Гетерогенный список инициализаторов - PullRequest
1 голос
/ 27 мая 2019

У меня есть вспомогательные классы 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" };

1 Ответ

3 голосов
/ 27 мая 2019

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

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"
};
...