2012-06-07 9 views
16

Wie kann ich eine aus einem Bereich initialisieren (wie durch ein Paar von Iteratoren definiert)?Initialisieren Sie std :: array mit einem Bereich (Paar von Iteratoren)

Etwas wie folgt aus:

vector<T> v; 
... 
// I know v has exactly N elements (e.g. I just called v.resize(N)) 
// Now I want a initialized with those elements 
array<T, N> a(???); // what to put here? 

Ich dachte array einen Konstruktor nimmt ein Paar von Iteratoren haben würde, so dass ich array<T, N> a(v.begin(), v.end()) tun könnte, aber es scheint, überhaupt keine Konstrukteure zu haben!

Ich weiß, ich kann copy den Vektor in das Array, aber ich würde lieber das Array mit dem Vektor Inhalt direkt initialisieren, ohne Standard-Konstruktion es zuerst. Wie kann ich?

+0

Gibt es einen Grund für diese Präferenz? Die Leistung wird fast genau gleich sein, da der Standardkonstruktor (normalerweise) nur die Basisstrukturen zuweist, die Sie sowieso benötigen. Es würde kein zusätzliches Zuweisen, Kopieren oder Freigeben geben. –

+1

@DavidSchwartz: Vielleicht habe ich eine const Array-Member in meiner Klasse und so muss ich es in der Initialisierungsliste und nicht den Konstruktor Körper initialisieren? – HighCommander4

+0

--- Können wir uns auf Random-Access-Iteratoren beschränken? Wenn das so ist, habe ich eine Lösung --- Es gibt keine Möglichkeit, die * Größe * zur Kompilierzeit zu bekommen. –

Antwort

17

Mit Random-Access-Iteratoren, und eine bestimmte Größe zur Compile-Zeit unter der Annahme, können Sie ein pack of indices, dies zu tun verwenden:

template <std::size_t... Indices> 
struct indices { 
    using next = indices<Indices..., sizeof...(Indices)>; 
}; 
template <std::size_t N> 
struct build_indices { 
    using type = typename build_indices<N-1>::type::next; 
}; 
template <> 
struct build_indices<0> { 
    using type = indices<>; 
}; 
template <std::size_t N> 
using BuildIndices = typename build_indices<N>::type; 

template <typename Iterator> 
using ValueType = typename std::iterator_traits<Iterator>::value_type; 

// internal overload with indices tag 
template <std::size_t... I, typename RandomAccessIterator, 
      typename Array = std::array<ValueType<RandomAccessIterator>, sizeof...(I)>> 
Array make_array(RandomAccessIterator first, indices<I...>) { 
    return Array { { first[I]... } }; 
} 

// externally visible interface 
template <std::size_t N, typename RandomAccessIterator> 
std::array<ValueType<RandomAccessIterator>, N> 
make_array(RandomAccessIterator first, RandomAccessIterator last) { 
    // last is not relevant if we're assuming the size is N 
    // I'll assert it is correct anyway 
    assert(last - first == N); 
    return make_array(first, BuildIndices<N> {}); 
} 

// usage 
auto a = make_array<N>(v.begin(), v.end()); 

Dies setzt voraus, einen Compiler der Lage, die Zwischen Kopien eliding. Ich denke, diese Annahme ist keine große Sache.

Tatsächlich kann es auch mit Eingabe-Iteratoren gemacht werden, da die Berechnung jedes Elements in einer braced-init-Liste vor der Berechnung des nächsten Elements (§8.5.4/4) sequenziert wird.

// internal overload with indices tag 
template <std::size_t... I, typename InputIterator, 
      typename Array = std::array<ValueType<InputIterator>, sizeof...(I)>> 
Array make_array(InputIterator first, indices<I...>) { 
    return Array { { (void(I), *first++)... } }; 
}  

Da *first++ hat keine I drin, wir brauchen eine Dummy-I die Expansion Pack zu provozieren. Komma-Operator zur Rettung, mit void(), Warnungen über Mangel an Effekten zum Schweigen zu bringen, und auch überladene Kommas zu verhindern.

+0

+1. Nett. (aber diesmal hasse ich diese Syntax: 'template >>>>'. Ich meine WTF.) – Nawaz

+0

Um zu verdeutlichen, 'BuildIndices' ist die' build_indices' Vorlage auf der verknüpften Seite? Könnten Sie es hier einfügen, um diese Antwort eigenständig zu machen? – ecatmur

+0

Siehe meine Lösung, die Boost verwendet. – Nawaz

4

Wie Sie bemerkt haben, hat std :: array überhaupt keine Konstruktoren (außer für den vom Compiler generierten Standardkonstruktor).

Dies wurde mit Absicht gemacht, so dass es wie ein C-Array statisch initialisiert werden kann. Wenn Sie das Array ohne einen statischen Initialisierer füllen möchten, müssen Sie Ihre Daten kopieren.

3

Sie können BOOST_PP_ENUM wie verwenden:

include <boost/preprocessor/repetition/enum.hpp> 

#define INIT(z, i, v) v[i] 

std::vector<int> v; 

//fill v with at least 5 items 

std::array<int,5> a = { BOOST_PP_ENUM(5, INIT, v) }; //MAGIC 

Hier wird die letzte Zeile erweitert, wie:

std::array<int,5> a = {v[0], v[1], v[2], v[3], v[4]}; //EXPANDED LINE 

das ist, was Sie wollen.

Verwandte Themen