使用带有使用constexpr递归定义的类型的C++11 initializer_list

Using a C++11 initializer_list with a recursively defined type using constexpr

本文关键字:C++11 list 类型 initializer 定义 constexpr 递归      更新时间:2023-10-16

是否可以使用C++11 initializer_list来组装递归定义的类,如下面的Foo,使用constexpr构造函数:

template <size_t N>
struct Foo {
  constexpr Foo(int x, Foo<N-1> f) : x(x), xs(xs) {}
  int x;
  Foo<N-1> xs;
};
template <> struct Foo<0> {};

我可以使用初始化Foo<3>

int main(int argc, char *argv[])
{
  Foo<3> a = Foo<3>(1,Foo<2>(2,Foo<1>(3,Foo<0>())));
  return 0;
}

使用Foo<3> a={1,2,3}。如果initializer_list中有一个constexpr tail函数,我认为它应该可以工作。

是的,以一种迂回的方式,有效地将初始值设定项列表拆包并重新打包为更合适的格式。然而,还有一种更好的(imho)方法:变分模板。

#include <stddef.h>
#include <iostream>
template <size_t N>
struct Foo {
  template<class... Tail>
  constexpr Foo(int i, Tail... t) : x(i), xs(t...) {}
  void print(){
    std::cout << "(" << x << ", ";
    xs.print();
    std::cout << ")";
  }
  int x;
  Foo<N-1> xs;
};
template <> 
struct Foo<1> {
  constexpr Foo(int i) : x(i) {}
  void print(){ std::cout << "(" << x << ")"; }
  int x;
};
int main(){
 Foo<3> x = {1, 2, 3};
 x.print();
 std::cout << "n";
}

预期输出:

(1,(2,(3))

请注意,我选择1作为基本情况,因为它更有意义。

我没有编译器可以编译它,但我认为正确的答案是:

template <size_t N>
struct Foo {
  constexpr Foo(int x, Foo<N-1> f)   //template iterator constructor
  : x(x), xs(xs) {}
  Foo(std::initializer_list<int> f)  //initializer list constructor
  : x(*f.begin()), xs(++f.begin(), f.end()) 
  { static_assert(xs.size()==N, "incorrect number of values in initializer list");}
  template<class iter>
  Foo(iter first, iter last)  //template iterator constructor
  : x(*first), xs(++first, last) {}  //UB if wrong number of values given
  int x;
  Foo<N-1> xs;
};
template <> 
struct Foo<1> { //I use 1 for smaller structures
  constexpr Foo(int f) 
  : x(f) {}
  Foo(std::initializer_list<int> f) 
  : x(*f.begin())
  { static_assert(xs.size()==1, "incorrect number of values in initializer list");}
  template<class iter>
  Foo(iter first, iter last)
  : x(*first)
  { assert(first+1 == last); } 
  int x;
};

对于递归结构,初始值设定项列表必须递归地传递给采用迭代器的构造函数。

解决方案是创建一个名为的函数

template<class T>
constexpr T initlist_val(initializer_list<T>& list, int index) {
  return (index < list.size()) ? *(list.begin() + index) : 0;
}

现在你可以去了

class MyClass {
public:
  int A, int B;
  constexpr MyClass(const initializer_list<int>& list) : A(initlist_val(list,0)), B(initlist_val(1)) {
  // Put nothing here etc..
  }
};

你不需要所有其他的东西。这可以与GCC一起工作,而不与其他任何东西一起测试。就规则而言,这可能是不正确的。