异构初始化列表

Heterogeneous initializer list

本文关键字:列表 初始化 异构      更新时间:2023-10-16

我有一个QueryField,选择用于构造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;
};

从上面的代码中看到的是QueryField对象的集合,可以这样初始化:

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

是否有可能消除明确指定QueryField的需求并以下初始化选择对象?

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

使用解决方案,您确实可以放下类型,但是您必须保留牙套:

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,并使用简单的情况和更明确的情况使用复杂的情况。

更明确。

为了允许真正的异源参数,我将使用variadic模板:

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