C++ 11使可变参数构造函数理解初始化列表的初始化列表

Ant*_*t6n 9 c++ templates variadic-templates c++11

假设我有一个Point类和一个Point数组,其中点数由模板参数给出.如何使用大括号进行初始化?如果我使用:

class Point {
public:
    float x, y;
    Point(float x, float y) : x(x), y(y) {}
};

template <size_t N>
class Array {
private:
    std::array<Point, N> _points;

public:
    template <typename... Points>
    Array(const Points& ... points) : _points({ points... }) {
    }
};
Run Code Online (Sandbox Code Playgroud)

然后这工作:

Array<2> a{Point{1,1}, Point{2, 2}};
Run Code Online (Sandbox Code Playgroud)

但如果我不提供显式Point对象,我在Xcode中出错:

Array<2> c{{1,1}, {2, 2}};
Run Code Online (Sandbox Code Playgroud)

错误是:"没有匹配的构造函数用于初始化Array <2>".对于特定的构造函数,它说"候选构造函数不可行:需要0个参数,但提供了2个".

我如何让它工作?

Bar*_*rry 12

为什么你的代码不起作用

你支持braced-init-list , {{1, 1}, {2, 2}}. 那东西没有类型.这只是一个东西的集合.模板推导不能真正起作用,因为它可以是任何东西 - 可以从该列表构造无限量的类型.即使你显然想要Points...成为Points,那也是行不通的.

怎么做呢

braced-init-list表示为任意数量的Ts(对于某些类型T)的唯一方法是使用std::initializer_list:

Array(std::initializer_list<Point> points) { ... }
Run Code Online (Sandbox Code Playgroud)

但是要从中初始化数组initializer_list,你需要类似的东西std::copy,但由于Point不是默认构造的,不幸的是,这是一个非首发.

我们可以直接拿一个数组:

Array(std::array<Point, N> const& a)
    : _points(a)
{ }
Run Code Online (Sandbox Code Playgroud)

这可以让你做你想做的事.或者至少,有一些额外的括号:

Array<2> c{{{{1, 1}, {2,2}}}};
Run Code Online (Sandbox Code Playgroud)

这是一个令人讨厌的过多的大括号!

所以我喜欢的一个技巧是实际创建一个带N Points 的构造函数,这就是你想要开始的.我们可以通过Array使用索引序列技巧进行部分特化来实现:

template <size_t N, class = std::make_index_sequence<N>>
class Array;

template <size_t N, size_t... Is>
class Array<N, std::index_sequence<Is...>> {
private:
    std::array<Point, N> _points;

    template <size_t>
    using Point_ = Point;

public:
    Array(Point_<Is>... points)
        : _points{{points...}}
    { }
};
Run Code Online (Sandbox Code Playgroud)

这里Point_<I>只是一个别名模板Point,我们只是将索引序列解压缩成一堆Points.因此,该构造函数是一个模板,它精确地采用N Points,然后我们就可以使用它,并使用大量的括号:

Array<2> c{{1, 1}, {2,2}}; // ok!
Run Code Online (Sandbox Code Playgroud)

嗯,不是真正的非首发.你可以做几件事来让它继续工作.你可以使Point默认构造,你可以写:

Array(std::initializer_list<Point> il)
{
    std::copy(il.begin(), il.begin() + std::min(il.size(), N),  _points.begin());
}
Run Code Online (Sandbox Code Playgroud)

或者即使没有使用Pointdefault-constructible,您也可以使用索引序列技巧在委托构造函数的帮助下进行初始化:

public:
    Array(std::initializer_list<Point> il)
        : Array(il, std::make_index_sequence<N>{})
    { }

private:
    template <size_t... Is>
    Array(std::initializer_list<Point> il, std::index_sequence<Is...> )
        : _points{{(Is < il.size() ? il.begin()[Is] : Point(0,0))...}}
    { }
Run Code Online (Sandbox Code Playgroud)