Reputation: 11056
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.
Upvotes: 2
Views: 2587
Reputation: 9
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.
Upvotes: 0
Reputation: 66961
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.
Upvotes: 1
Reputation: 131829
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.
Upvotes: 9