【问题标题】:Class with nested brace-enclosed initializers带有嵌套大括号的初始化器的类
【发布时间】:2023-04-10 13:17:01
【问题描述】:

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

#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

有什么办法可以做到吗?

【问题讨论】:

    标签: c++ c++11


    【解决方案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 }} }} }};
    }
    

    我所做的是删除了继承和自定义构造函数,这使得它可以使用编译器自己对聚合初始化的支持来工作。

    不幸的是,需要多个大括号这一事实,但我认为没有一种简单的方法可以避免这种情况,而且在我看来,这对可读性没有太大影响。 p>

    稍微复杂一点的解决方案,但仍然不是很复杂,就是不嵌套multi&lt;...&gt;,而是使用多维数组:

    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&lt;2, 2, 2&gt; 现在只是int[2][2][2],你不能再添加任何方法(或任何类似的东西)。

    【讨论】:

      【解决方案2】:

      您正在尝试像聚合一样初始化您的类,但它不是聚合,因为它有一个用户提供的构造函数。

      正如 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]);
              }
          }
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-04
        • 1970-01-01
        • 2016-12-11
        • 2014-07-31
        • 1970-01-01
        • 2022-01-18
        相关资源
        最近更新 更多