2016-11-30 1 views
2

Lassen Sie mich erklären, was ich mit Code suche. Lassen Sie uns nehme an wir eine Vorlage haben eine Reihe Einwickeln:Sub-Array-Vorlage

template <typename T, std::size_t SIZE> 
struct wrap 
{ 
    using value_type = T; 
    using wrap_type = wrap; 

    static constexpr auto size = SIZE; 
    T data[size]{}; 
}; 

Ich möchte eine Funktion zum wrap Vorlage hinzufügen, sollte diese Funktion ein Objekt der Lage zurückzukehren einen bestimmten Teil des Arrays in wrap, die Funktion enthalten sind, zu manipulieren wissen sollte, wo das Sub-Array beginnt und wie viele Elemente wird es haben, so dachte ich über eine Funktion wie folgt aus:

template <typename T, std::size_t SIZE> 
struct wrap 
{ 
    using value_type = T; 
    using wrap_type = wrap; 

    static constexpr auto size = SIZE; 
    T data[size]{}; 

    template <std::size_t START, std::size_t COUNT> 
    ???? sub() { ... } 
}; 

ich will nicht das ???? Objekt von wrap::sub zurück ein paar Hinweise haben, aber ein statisches Array, so war mein Ansatz:

template <typename T, std::size_t SIZE> 
struct wrap 
{ 
    using value_type = T; 
    using wrap_type = wrap; 

    static constexpr auto size = SIZE; 
    T data[size]{}; 

    template <std::size_t START, std::size_t COUNT> 
    struct sub_array 
    { 
     using value_type = wrap_type::value_type *; 

     static constexpr auto start = START; 
     static constexpr auto count = COUNT; 

     value_type data[count]{}; 
    }; 

    template <std::size_t START, std::size_t COUNT> 
    sub_array<START, COUNT> sub() { ... } 
}; 

Und hier ist das Problem: Was soll ich in wrap::sub schreiben? Ich bin mir ziemlich sicher, dass ein Ansatz mit std::integer_sequence möglich sein sollte, aber ich habe keine Erfahrung damit, daher weiß ich nicht einmal, was ich versuchen soll. Ich habe versucht, einen Wert von 2 mit beiden Template-Parameter in wrap::sub asume und es funktionierte wie erwartet:

template <std::size_t START, std::size_t SIZE> 
sub_array<START, SIZE> sub() { return { &data[START], &data[START + 1u] }; } 

ich habe es ausprobiert wie ich weiter unten zeigen:

wrap<int, 9> w{1, 2, 3, 4, 5, 6, 7, 8, 9}; 

// x is the view [3, 4] 
auto x = w.sub<2, 2>(); 

std::cout << *x.data[0] << '\n'; // shows 3 

Also, konzeptionell die Körper von wrap::sub sollte sein:

return { &data[START + 0], &data[START + 1], ... &data[START + n] }; 

Wie soll ich die Wirkung oben erreichen?

+0

Ein Sub-Array, das ein tatsächliches Array ist, ist keine Ansicht wie in 'string_view'. Es ist eine Kopie eines Teils des Arrays. Dies sind unterschiedliche Konzepte. Bitte klären Sie mit dem Rückgabewert von 'sub()', welche genauen Eigenschaften Sie haben möchten. – Yakk

+0

Die Eigenschaften sind: ein Objekt zu haben, das in der Lage ist, einen Unterteil des im 'wrap'-Objekt enthaltenen Arrays zu manipulieren. Ich werde die Referenz "string_view" loswerden. –

+1

Ihr Entwurf gibt eine Kopie des Unterteils des Arrays zurück. Änderungen an diesem Unterpart werden nicht im ursprünglichen Array angezeigt, da Sie eine Kopie erstellt haben. – Yakk

Antwort

1

Dies löst das Problem, wie oben beschrieben:

template<class=void, std::size_t...Is> 
auto indexer(std::index_sequence<Is...>) { 
    return [](auto&& f){ 
    return decltype(f)(f)(std::integral_constant<std::size_t, Is>{}...); 
    }; 
} 
template<std::size_t N> 
auto indexer() { 
    return indexer(std::make_index_sequence<N>{}); 
} 

Auf diese Weise können Sie eine erweiterbare Parameter Packung von Kompilierung-Zahlen erstellen, ohne eine benutzerdefinierte Hilfefunktion jedes Mal zu schreiben.

Es ist gültig C++ 14, aber einige Compiler (wie MSVC) behaupten, C++ 14 Compiler zu sein, ohne tatsächlich C++ 14 Compiler zu sein.

template <std::size_t START, std::size_t SIZE> 
sub_array<START, SIZE> sub() const { 
    auto index = indexer<SIZE>(); 
    return index(
    [&](auto...Is)->sub_array<START, SIZE> 
    { 
     return {data[START+Is]...}; 
    } 
); 
} 

indexer<N> liefert einen Indexer, dass, wenn eine Lambda weitergegeben, es mit std::integral_constant<std::size_t, 0> durch std::integral_constant<std::size_t, N-1> aufruft.

Dieses Paket kann dann inline in der Funktion erweitert werden, die indexer erstellt.

In den Kommentaren, die Sie erwähnen, möchten Sie, dass Änderungen am Sub-Array im ursprünglichen Array widergespiegelt werden.

Ihr Design lässt dies nicht zu. Das Sub-Array ist eine Kopie eines Slice des ursprünglichen Arrays.

Der richtige Weg dazu ist, dass Ihr Sub-Array ein Paar Zeiger in Ihrem ursprünglichen Array ist.

template<class T> 
struct array_view { 
    T* b = 0; 
    T* e = 0; 

    T* begin() const { return b; } 
    T* end() const { return e; } 

    T& operator[](std::size_t i)const { return begin()[i]; } 

    bool empty() const { return begin()==end(); } 
    std::size_t size() const { return end()-begin(); } 

    array_view(T* s, T* f):b(s), e(f) {} 
    array_view(T* s, std::size_t l):array_view(s, s+l) {} 

    array_view()=default; 
    array_view(array_view const&)=default; 
    array_view& operator=(array_view const&)=default; 
}; 

Ein array_view ist eine kompakte Art und Weise zu einer Scheibe von einem Array zu sprechen.

Wenn Sie Schritt gehen wollen, müssen Sie ein bisschen mehr Arbeit erledigen.

+0

Sie haben einen kleinen Tippfehler in 'return {data [START + Is] ...};' Bei meinem Ansatz kopiere ich kein Stück des ursprünglichen Arrays, sondern ein Array von Zeigern auf die Werte des ursprünglichen Arrays ('{& data [START + Is] ...}'), so dass ich am Ende das ursprüngliche Array über den Zeiger ändern kann. –

+0

@PaperBirdMaster Warte, oh, das ist schlecht. Einen Stapel von Zeigern zu speichern, die sich durch "sizeof (T)" unterscheiden, ist ein Anti-Pattern; Das Speichern eines Anfangs-/Endzeigers ist einfacher, leistungsfähiger und speichert weniger sinnlosen Zustand. – Yakk

+0

Huh ... Ich kannte das Ding nicht, das du erwähnt hast. Ich dachte, dass jede Adresse innerhalb des 'sub_array' gültig sein sollte. Haben Sie eine Verbindung mit Informationen über das Thema, um mich zu erleuchten? –

1

Ich bin mir ziemlich sicher, dass ein Ansatz std::integer_sequence mit möglich ... es ist

Hier sollte:

template <typename T, std::size_t SIZE> 
struct wrap 
{ 
    using value_type = T; 
    using wrap_type = wrap; 

    static constexpr auto size = SIZE; 
    T data[size]{}; 

    template <std::size_t START, std::size_t COUNT> 
    struct sub_array 
    { 
     using value_type = wrap_type::value_type *; 

     static constexpr auto start = START; 
     static constexpr auto count = COUNT; 

     value_type data[count]{}; 
    }; 

    template <std::size_t START, std::size_t COUNT> 
    constexpr sub_array<START, COUNT> sub() { 
     static_assert(START + COUNT <= SIZE, "sub_array is out of range"); 
     // The argument makes an instance of integer sequence from 0 to COUNT-1 
     return sub_impl<START, COUNT>(std::make_index_sequence<COUNT>{}); 
    } 

private: 

    template <std::size_t START, std::size_t COUNT, std::size_t ... Is> 
    constexpr sub_array<START, COUNT> sub_impl(std::index_sequence<Is...>) { 
     // Arithmetic in argument list expansion is pretty valid. 
     return {&data[Is+START]...}; 
    } 
}; 

nun die folgenden Zeilen:

wrap<double, 10> ds{1,2,3,4,5,6,7,8,9,10}; 
auto s = ds.sub<2,3>(); 
std::cout << *s.data[0] << std::endl; 

Drucken 3. Dieser Code ist genau eine Anpassung von documentation examples für Ihre Struktur. Wahrscheinlich kann es verkürzt werden. Und ich bin sicher, dass mehr statische (Kompilierungszeit) Assertions in Ihrem Code deklariert werden können.

Endlich, nach @ Yakks Kommentaren, gibt diese Implementierung von sub eine Subarray-Kopie zurück, keine Ansicht.