具有嵌套大括号的初始化程序的类

时间:2014-02-22 20:07:53

标签: c++ c++11

我想使用可变参数模板创建一个递归的多维数组类。

#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

有没有办法实现这个目标?

2 个答案:

答案 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]);
        }
    }
};