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

时间:2016-03-30 22:10:14

标签: c++ templates c++11 variadic-templates

假设我有一个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... }) {
    }
};

然后这个有效:

Array<2> a{Point{1,1}, Point{2, 2}};

但如果我没有提供明确的Point个对象,我会在Xcode中收到错误:

Array<2> c{{1,1}, {2, 2}};

错误是:“没有匹配的构造函数用于初始化Array&lt; 2&gt;”。对于特定的构造函数,它说“候选构造函数不可行:需要0个参数,但提供了2个”。

如何让它发挥作用?

1 个答案:

答案 0 :(得分:12)

为什么代码不能正常工作

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

做什么

braced-init-list 表示任意数量的T s(对于某些类型T)的唯一方法是使用{{1} }:

std::initializer_list

但要从Array(std::initializer_list<Point> points) { ... } 初始化数组,您需要类似initializer_list的内容,但由于std::copy不是默认可构造的,不幸的是,这是一个非首发< SUP>†。

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

Point

这可以让你做你想做的事。或者至少,还有一些额外的支撑:

Array(std::array<Point, N> const& a)
    : _points(a)
{ }

这是一个令人烦恼的过多的大括号!

所以我喜欢的一个技巧是实际创建一个带Array<2> c{{{{1, 1}, {2,2}}}}; N s的构造函数,这是你想要开始的。我们可以通过使用索引序列技巧Point进行部分特化来实现这一点:

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...}} { } }; 只是Point_<I>的别名模板,我们只是将索引序列解压缩为一堆Point。因此,该构造函数是一个 -template,它精确地采用Point N s,然后我们就可以使用它,并使用大量的括号:

Point

嗯,真的不是非首发。你可以做几件事来让它继续工作。您可以使Array<2> c{{1, 1}, {2,2}}; // ok! 默认可构造,此时您可以写:

Point

或者即使不使Array(std::initializer_list<Point> il) { std::copy(il.begin(), il.begin() + std::min(il.size(), N), _points.begin()); } 默认可构造,也可以使用索引序列技巧在委托构造函数的帮助下进行初始化:

Point