Использование C ++ 11 initializer_list с рекурсивно определенным типом с использованием constexpr - PullRequest
2 голосов
/ 13 марта 2012

Можно ли использовать 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}. Если в * 1012 была constexpr tail функция, думаю, она должна работать.

Ответы [ 3 ]

9 голосов
/ 13 марта 2012

Да, в некотором роде, эффективно распаковывает и перепаковывает список инициализаторов в более подходящий формат. Однако есть лучший (imho) способ: шаблоны Variadic.

#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 в качестве базового варианта, поскольку он просто более понятен.

1 голос
/ 13 марта 2012

У меня нет компилятора, который мог бы скомпилировать его, но я думаю, что правильный ответ - что-то вроде:

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

Для рекурсивной структуры список инициализатора должен быть передан конструктору, который рекурсивно принимает итераторы.

0 голосов
/ 01 июля 2014

Решение состоит в том, чтобы сделать функцию с именем

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, не проверенным с чем-либо еще.Вероятно, это не правильно с точки зрения правил.

...