我想要一个可以从任何序列初始化的类,这意味着可以从任何类型初始化,该类型具有返回迭代器的begin()和end()方法。
template<typename B>
class A
{
public:
A(any_sequence_type<B> arglist)
{
for(B b : arglist)
init_members(b);
}
}
我希望编译以下内容:
A<int> a = {1, 2, 3};
A<int> b = std::vector<int>();
A<Something> c = MyClassWithBeginAndEnd();
可能吗?
编辑:
使用模板构造函数的建议解决方案不起作用。GCC表示:模板参数推断/替换失败:无法推断模板参数“t”。Clang说:已忽略候选模板:无法推断模板参数“t”。
但是,如果我显式地强制转换初始值设定项,事情就会开始工作。
A a = A({B(), B(), B()}); // does not work
A a = (std::initializer_list<B>){B(), B(), B()}; // works
为什么呢?
使用模板构造函数:
template<typename B>
class A
{
public:
template <typename T>
A(T&& arglist)
{
for (auto&& b : arglist) {
init_members(b);
}
}
// overload for initializer_list
A(std::initializer_list<B> arglist) {
for (auto&& b : arglist) {
init_members(b);
}
}
};
如果T
没有提供begin
/end
并且内容不能转换为B
,则会出现错误。
您可以使用SFINAE来限制允许的类型T
,因此在误用的情况下,调用站点而不是构造函数内部会出错。
演示
对于a::a
接受的类型,您需要一个模板参数。如果Seq
不是合适的序列类型,则应使用SFINAE禁用此构造函数;否则,您将无法通过复制构造函数获得重载解决方案。
template <typename Seq,
typename = std::enable_if_t<
std::is_convertible<decltype(*std::declval<Seq>().begin()), B>::value &&
std::is_convertible<decltype(*std::declval<Seq>().end()), B>::value>>
A(Seq arglist) {
for (B b : arglist) {
init_members(b);
}
}
也要考虑使用<代码> const Seq
如果您希望它也能与带括号的init列表一起工作,那么必须添加一个单独的std::initializer\u list
构造函数。这是因为大括号的init列表不是表达式,也没有类型;这基本上总是一个特例。