【问题标题】:Heterogeneous initializer list异构初始化列表
【发布时间】:2019-10-13 03:19:34
【问题描述】:

我有一个用于构造 SQL 语句的 QueryField 和 Select 辅助类:

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 中是不可能的
  • 如果我错了,请纠正我,但你想要一个同质(相同类型)而不是异构(不同类型)列表
  • 你用的是什么编译器?对我来说似乎编译得很好? godbolt.org/z/XwgbD-?还是我缺少什么?

标签: c++ c++17 initializer-list


【解决方案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)) 停止编译。我将两个构造函数都声明为默认值,但它没有帮助。
  • 可能的解决方案是让它不是构造函数,而是像 MakeSelect(Args && ... fields) 这样的函数(像 std::make_shared)
  • 啊,是的,您必须过滤掉移动和复制构造函数。我编辑了答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-07
  • 1970-01-01
  • 2012-03-19
  • 1970-01-01
  • 2022-01-19
  • 2010-10-16
  • 1970-01-01
相关资源
最近更新 更多