Konstruktionen mit Hilfe einer std::initializer_list

Content

Ein traditionelles Array lässt sich direkt mit einer Werteliste konstruieren:

int values[] = {2, 3, 5, 7, 11, 13};

Es stellt sich daher die Frage, ob dies auch mit selbst-definierten Klassen möglich ist. Und in der Tat wurde dies durch die Einführung der vom Übersetzer unterstützten std::initializer_list<T>-Klasse möglich, die über #include <initializer_list> zur Verfügung steht.

Hier ist ein triviales Beispiel, das die Benutzung zeigt:

#include <initializer_list>
#include <iostream>

template<typename T>
struct Average {
   Average(std::initializer_list<T> list) : result(0) {
      T sum{};
      for (auto it = list.begin(); it != list.end(); ++it) {
	 sum += *it;
      }
      result = sum / list.size();
   }
   T result;
};

int main() {
   Average<double> avg = {1, 2, 3, 4};
   std::cout << avg.result << std::endl; // delivers 2.5
}

Wenn der Übersetzer eine in geschweiften Klammern gefasste Initialisierung sieht und es einen passenden Konstruktor mit einer std::initializer_list gibt, wird dieser ausgewählt. Dies hat Vorrang vor den anderen Konstruktoren. Die Nutzung regulärer Konstruktoren ohne eine std::initializer_list kann durch den Einsatz runder Klammern erzwungen werden.

Ferner ist auch die Verwendung von std::initializer_list bei Zuweisungen, Parameterübergaben und bei einer ranged for loop möglich.

Weitere Infos zu dieser Klasse: http://en.cppreference.com/w/cpp/utility/initializer_list

Aufgaben

Vorlage

#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <iterator>
#include <new>
#include <utility>

template<typename T>
class Array {
   private:
      using Tag = enum {AllocateStorage};
      Array(Tag tag, std::size_t size) :
	 size(size),
	 data(size > 0?
	       static_cast<T*>(operator new(sizeof(T) * size))
	    :
	       nullptr) {
      }
   public:
      Array() : size(0), data(nullptr) {
      }
      Array(std::size_t size) : Array(AllocateStorage, size) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T();
	 }
      }
      Array(std::size_t size, const T& t) : Array(AllocateStorage, size) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T(t);
	 }
      }
      template<typename Iterator>
      Array(Iterator it1, Iterator it2) :
	    Array(AllocateStorage, std::distance(it1, it2)) {
	 std::size_t index = 0;
	 while (it1 != it2 && index < size) {
	    new (data + index++) T(*it1++);
	 }
	 size = index; // just in case
      }
      Array(const Array& other) : Array(AllocateStorage, other.size) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T(other.data[index]);
	 }
      }
      friend void swap(Array& a1, Array& a2) {
	 using std::swap;
	 swap(a1.size, a2.size);
	 swap(a1.data, a2.data);
      }
      Array(Array&& other) : Array() {
	 swap(*this, other);
      }
      ~Array() {
	 for (std::size_t index = 0; index < size; ++index) {
	    data[index].~T();
	 }
	 operator delete(data);
      }
      Array& operator=(Array other) {
	 swap(*this, other);
	 return *this;
      }
      std::size_t get_size() const {
	 return size;
      }
      T& operator()(std::size_t index) {
	 assert(index < size);
	 return data[index];
      }
      const T& operator()(std::size_t index) const {
	 assert(index < size);
	 return data[index];
      }
      T* begin() {
	 return data;
      }
      const T* begin() const {
	 return data;
      }
      T* end() {
	 return data + size;
      }
      const T* end() const {
	 return data + size;
      }
   private:
      std::size_t size;
      T* data;
};

#endif