提问者:小点点

可从任何序列类型初始化的C类


我想要一个可以从任何序列初始化的类,这意味着可以从任何类型初始化,该类型具有返回迭代器的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

为什么呢?


共2个答案

匿名用户

使用模板构造函数:

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列表不是表达式,也没有类型;这基本上总是一个特例。

相关问题