C ++ 11使可变参数构造函数理解初始化列表的初始化列表
C++11 making variadic constructor understand an initialization list of initialization lists
假设我有一个 class 点和一个点数组,其中点数由模板参数给出。如何使用大括号进行初始化工作?
如果我使用:
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}};
错误是:"No matching Constructor for initialization of Array<2>"。对于特定的构造函数,它表示 "Candidate constructor not viable: requires 0 arguments, but 2 were provided".
如何让它工作?
为什么你的代码不起作用
您正在 braced-init-list、{{1, 1}, {2, 2}}
中进行支撑。那东西没有类型。这只是一些东西的集合。模板推导不能真正起作用,因为它可能是 任何东西 - 可以从该列表构造无限数量的类型。即使您明确希望 Points...
成为 Point
,那也行不通。
改为做什么
将 braced-init-list 表示任意数量的 T
的唯一方法(对于某些类型 T
)是使用 std::initializer_list
:
Array(std::initializer_list<Point> points) { ... }
但是要从 initializer_list
初始化数组,您需要像 std::copy
这样的东西,但是由于 Point
不是默认构造的,所以这是一个非启动器不幸的是†。
我们可以直接取一个数组:
Array(std::array<Point, N> const& a)
: _points(a)
{ }
这让您可以随心所欲。或者至少,加上一些额外的大括号:
Array<2> c{{{{1, 1}, {2,2}}}};
牙套的数量多得令人恼火!
所以我喜欢的一个技巧是实际创建一个采用 N
Point
s 的构造函数,这正是您想要开始的。我们可以通过使用索引序列技巧使 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
的别名模板,我们用它只是将索引序列解压成一堆Point
。因此,该构造函数是一个 non-template,它精确地采用 N
Point
s,然后我们可以将它与合理数量的大括号一起使用:
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
可默认构造,您也可以使用索引序列技巧在委托构造函数的帮助下进行初始化:
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))...}}
{ }
假设我有一个 class 点和一个点数组,其中点数由模板参数给出。如何使用大括号进行初始化工作? 如果我使用:
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}};
错误是:"No matching Constructor for initialization of Array<2>"。对于特定的构造函数,它表示 "Candidate constructor not viable: requires 0 arguments, but 2 were provided".
如何让它工作?
为什么你的代码不起作用
您正在 braced-init-list、{{1, 1}, {2, 2}}
中进行支撑。那东西没有类型。这只是一些东西的集合。模板推导不能真正起作用,因为它可能是 任何东西 - 可以从该列表构造无限数量的类型。即使您明确希望 Points...
成为 Point
,那也行不通。
改为做什么
将 braced-init-list 表示任意数量的 T
的唯一方法(对于某些类型 T
)是使用 std::initializer_list
:
Array(std::initializer_list<Point> points) { ... }
但是要从 initializer_list
初始化数组,您需要像 std::copy
这样的东西,但是由于 Point
不是默认构造的,所以这是一个非启动器不幸的是†。
我们可以直接取一个数组:
Array(std::array<Point, N> const& a)
: _points(a)
{ }
这让您可以随心所欲。或者至少,加上一些额外的大括号:
Array<2> c{{{{1, 1}, {2,2}}}};
牙套的数量多得令人恼火!
所以我喜欢的一个技巧是实际创建一个采用 N
Point
s 的构造函数,这正是您想要开始的。我们可以通过使用索引序列技巧使 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
的别名模板,我们用它只是将索引序列解压成一堆Point
。因此,该构造函数是一个 non-template,它精确地采用 N
Point
s,然后我们可以将它与合理数量的大括号一起使用:
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
可默认构造,您也可以使用索引序列技巧在委托构造函数的帮助下进行初始化:
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))...}}
{ }