异构初始化列表
Heterogeneous initializer list
我有一个 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" };
使用您的解决方案,您确实可以删除类型,但必须保留大括号:
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)}...} {}
如果要保留copy/move构造函数,必须过滤掉一些参数类型:
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"
};
我有一个 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" };
使用您的解决方案,您确实可以删除类型,但必须保留大括号:
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)}...} {}
如果要保留copy/move构造函数,必须过滤掉一些参数类型:
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"
};