我想使用可变参数模板创建一个递归的多维数组类。
#include <array>
template<int...> struct multi;
template<int Body>
struct multi<Body>: std::array<int, Body> {
template<typename... Params>
multi(int first, const Params&... args)
: std::array<int, Body> {{ first, args... }} {
}
};
template<int Head, int Body, int... Tail>
struct multi<Head, Body, Tail...>: std::array<multi<Body, Tail...>, Head> {
template<typename... Params>
multi(const multi<Body, Tail...>& first, const Params&... args)
: std::array<multi<Body, Tail...>, Head> {{ first, args... }} {
}
};
要初始化此类的实例,我必须执行以下操作:
multi<2, 2> m { multi<2>{ 1, 2 }, multi<2>{ 3, 4 } };
我真的很感激,如果我可以像这样使用类似C数组的样式进行统一初始化:
multi<2, 2> m { { 1, 2 }, { 3, 4 } };
不幸的是,这不起作用,因为似乎无法处理这些嵌套的初始化者:
multi.cc: In function 'int main()':
multi.cc:25:40: error: no matching function for call to 'multi<2, 2>::multi(<brace-enclosed initializer list>)'
multi<2, 2> m { { 1, 2 }, { 3, 4 } };
^
multi.cc:25:40: note: candidates are:
multi.cc:19:5: note: multi<Head, Body, Tail ...>::multi(const multi<Body, Tail ...>&, const Params& ...) [with Params = {}; int Head = 2; int Body = 2; int ...Tail = {}]
multi(const multi<Body, Tail...>& first, const Params&... args)
^
multi.cc:19:5: note: candidate expects 1 argument, 2 provided
multi.cc:17:8: note: constexpr multi<2, 2>::multi(const multi<2, 2>&)
struct multi<Head, Body, Tail...>: std::array<multi<Body, Tail...>, Head> {
^
multi.cc:17:8: note: candidate expects 1 argument, 2 provided
multi.cc:17:8: note: constexpr multi<2, 2>::multi(multi<2, 2>&&)
multi.cc:17:8: note: candidate expects 1 argument, 2 provided
有没有办法实现这个目标?
答案 0 :(得分:1)
如果你不介意多个括号,你可以非常接近:
template<int...> struct multi;
template <int N>
struct multi<N> {
int elems[N];
};
template <int Head, int... Tail>
struct multi<Head, Tail...> {
multi<Tail...> elems[Head];
};
int main() {
multi<2, 2, 2> m {{ {{ {{ 1, 2 }}, {{ 3, 4 }} }}, {{ {{ 5, 6 }}, {{ 7, 8 }} }} }};
}
我所做的是删除了继承和自定义构造函数,这使得编译器自己支持聚合初始化成为可能。
需要多个括号的事实是不幸的,但我认为没有一种简单的方法可以避免这种情况,并且在我看来, 对可读性的影响很小。
稍微复杂的解决方案,但仍然不是很复杂,不是嵌套multi<...>
,而是使用多维数组:
template<int...> struct helper;
template<int N>
struct helper<N> { typedef int type[N]; };
template<int Head, int... Tail>
struct helper<Head, Tail...> { typedef typename helper<Tail...>::type type[Head]; };
template<int... Ns>
struct multi {
typename helper<Ns...>::type elems;
};
int main() {
multi<2> m1 { 1, 2 };
multi<2, 2> m2 {{ { 1, 2 }, { 3, 4 } }};
multi<2, 2, 2> m3 {{ { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }};
}
你仍然需要多个大括号,但只在外层。
如果事实证明你实际上根本不需要课程,那么可以做到:
template<int...> struct helper;
template<int N>
struct helper<N> { typedef int type[N]; };
template<int Head, int... Tail>
struct helper<Head, Tail...> { typedef typename helper<Tail...>::type type[Head]; };
template<int... Ns>
using multi = typename helper<Ns...>::type;
int main() {
multi<2> m1 { 1, 2 };
multi<2, 2> m2 { { 1, 2 }, { 3, 4 } };
multi<2, 2, 2> m3 { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };
}
但由于multi<2, 2, 2>
现在仅为int[2][2][2]
,因此您无法再添加任何方法(或任何类似方法)。
答案 1 :(得分:0)
您正在尝试像聚合一样初始化您的类,但它不是聚合,因为它具有用户提供的构造函数。
您可以删除用户定义的构造函数,并使您的类成为聚合,正如hvd解释的那样。或者,您可以使构造函数采用std::initializer_list
。但是,这种方法有一些警告......
#include <array>
#include <initializer_list>
#include <algorithm>
template<int...> struct multi;
template<int Body>
struct multi<Body>: std::array<int, Body> {
multi() = default;
multi(std::initializer_list<int> l) {
std::copy(l.begin(), l.end(), std::array<int, Body>::begin());
}
};
template<int Head, int Body, int... Tail>
struct multi<Head, Body, Tail...>: std::array<multi<Body, Tail...>, Head> {
multi() = default;
multi(std::initializer_list<multi<Body, Tail...>> l) {
for (int i = 0; i < Head; i++) {
(*this)[i] = multi<Body, Tail...>(l.begin()[i]);
}
}
};
int main() {
multi<2, 2> m {{1, 2}, {3, 4}}; // OK
multi<2, 1000> m2 {{1, 2}, {3, 4}}; // constructs temporary arrays of size 1000 and copies them---expensive!
multi<2> m3 {1, 2, 3}; // too many initializers, but no compile error!
}
可以通过以初始形式传递初始化列表,一个(可能)嵌套的int初始化列表,并在每个级别进行迭代来解决不必要复制的问题。但在这种情况下,模板参数推导不起作用,您需要一个辅助类。此外,如果给出的初始化程序太多,您仍然无法触发编译错误。
#include <array>
#include <initializer_list>
#include <algorithm>
// IL<n>::type is an n-times nested initializer list of ints
template<int n> struct IL {
typedef std::initializer_list<typename IL<n-1>::type> type;
};
template<> struct IL<1> {
typedef std::initializer_list<int> type;
};
template<int...> struct multi;
template<int Body>
struct multi<Body>: std::array<int, Body> {
multi() = default;
multi(const std::initializer_list<int>& l) {
assign(l);
}
void assign(const std::initializer_list<int>& l) {
std::copy(l.begin(), l.end(), std::array<int, Body>::begin());
}
};
template<int Head, int Body, int... Tail>
struct multi<Head, Body, Tail...>: std::array<multi<Body, Tail...>, Head> {
multi() = default;
multi(const typename IL<2 + sizeof... Tail>::type& l) {
assign(l);
}
void assign(const typename IL<2 + sizeof... Tail>::type& l) {
for (int i = 0; i < l.size(); i++) {
(*this)[i].assign(l.begin()[i]);
}
}
};