Question

Is it possible to use the C++11 initializer_list to assemble a recursively defined class such as Foo, below, using constexpr constructors:

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> {};

I can initialise a Foo<3> using:

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

It would be nice to use Foo<3> a = {1,2,3} instead. If there was a constexpr tail function in initializer_list I think it should work.

Was it helpful?

Solution

Yes, in a kind of round-about way, effectively unpacking and repacking the initializer list to a more suited format. However, there is a better (imho) way: Variadic templates.

#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";
}

Output as expected:

(1, (2, (3)))

Note that I chose 1 as the base case, as it simply makes more sense.

OTHER TIPS

I don't have a compiler that could compile it, but I think the correct answer is something along the lines of:

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;
};

For a recursive structure, the initializer list would have to pass to a constructor that takes iterators, recursively.

The solution is to make a function called

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

Now you can go

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..
  }

};

You do not need all the other stuff. This can work with GCC not tested with anything else. Probably is not correct in terms of the rules.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top