Details zu std::make_index_sequence und std::index_sequence

Lesezeit: 5 Minuten

Details zu stdmake index sequence und
Benutzer3613174

Ich genieße es, mich mit variadischen Vorlagen zu beschäftigen, und habe angefangen, mit dieser neuen Funktion herumzuspielen. Ich versuche, meinen Kopf um die Implementierungsdetails von zu bekommen std::index_sequence‘s (wird für die Tupelimplementierung verwendet). Ich sehe dort Beispielcode, aber ich möchte wirklich eine verdummte Schritt-für-Schritt-Erklärung, wie ein std::index_sequence ist kodiert und das betreffende Meta-Programmierprinzip für jede Stufe. Überlegen Ja wirklich verdummt 🙂

1642851608 417 Details zu stdmake index sequence und
max66

Ich sehe dort Beispielcode, aber ich möchte wirklich eine verdummte Schritt-für-Schritt-Erklärung, wie eine index_sequence codiert wird, und das fragliche Meta-Programmierprinzip für jede Phase.

Was Sie fragen, ist nicht gerade trivial zu erklären …

Brunnen… std::index_sequence selbst ist sehr einfach: ist wie folgt definiert

template<std::size_t... Ints>
using index_sequence = std::integer_sequence<std::size_t, Ints...>;

das ist im Wesentlichen ein Vorlagencontainer für vorzeichenlose Ganzzahlen.

Der knifflige Teil ist die Implementierung von std::make_index_sequence. Das heißt: Der knifflige Teil ist Pass aus std::make_index_sequence<N> zu std::index_sequence<0, 1, 2, ..., N-1>.

Ich schlage Ihnen eine mögliche Implementierung vor (keine großartige Implementierung, aber (hoffentlich) einfach zu verstehen) und ich werde versuchen zu erklären, wie sie funktioniert.

Nicht genau die Standard-Index-Sequenz, die abgeht std::integer_sequence, aber Festsetzung der std::size_t Typ können Sie eine vernünftige bekommen indexSequence/makeIndexSequence koppeln Sie mit dem folgenden Code.

// index sequence only
template <std::size_t ...>
struct indexSequence
 { };

template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
 { };

template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
 { using type = indexSequence<Next ... >; };

template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;

Ich nehme an, dass ein guter Weg, um zu verstehen, wie es funktioniert, darin besteht, einem praktischen Beispiel zu folgen.

Wir können Punkt für Punkt sehen, wie makeIndexSequence<3> werden index_sequenxe<0, 1, 2>.

  • Wir haben das makeIndexSequence<3> ist definiert als typename indexSequenceHelper<3>::type [N is 3]

  • indexSequenceHelper<3> stimmen nur mit dem allgemeinen Fall überein, also erben von indexSequenceHelper<2, 2> [N is 3 and Next... is empty]

  • indexSequenceHelper<2, 2> stimmen nur mit dem allgemeinen Fall überein, also erben von indexSequenceHelper<1, 1, 2> [N is 2 and Next... is 2]

  • indexSequenceHelper<1, 1, 2> stimmen nur mit dem allgemeinen Fall überein, also erben von indexSequenceHelper<0, 0, 1, 2> [N is 1 and Next... is 1, 2]

  • indexSequenceHelper<0, 0, 1, 2> Passen Sie beide Fälle (allgemeine und partielle Spezialisierung) an, damit die partielle Spezialisierung angewendet und definiert wird type = indexSequence<0, 1, 2> [Next... is 0, 1, 2]

Fazit: makeIndexSequence<3> ist indexSequence<0, 1, 2>.

Hoffe das hilft.

— BEARBEITEN —

Einige Klarstellungen:

  • std::index_sequence und std::make_index_sequence sind ab C++14 verfügbar

  • Mein Beispiel ist (hoffentlich) einfach zu verstehen, hat aber (wie von Aschepler gezeigt) die große Grenze, die eine lineare Implementierung ist; Ich meine: wenn nötig index_sequence<0, 1, ... 999>, verwenden makeIndexSequence<1000> Sie implementieren rekursiv 1000 verschiedene indexSequenceHelper; aber es gibt eine Rekursionsgrenze (Compiler von Compiler unterschiedlich), die kleiner als 1000 sein kann; Es gibt andere Algorithmen, die die Anzahl der Rekursionen begrenzen, aber komplizierter zu erklären sind.

  • Eine gute Antwort, aber ich würde hinzufügen, dass es einen Grund gibt, warum einige Implementierungen, die Sie möglicherweise finden, etwas komplizierter sind: Sie versuchen, die Anzahl und/oder Tiefe der Vorlageninstanziierungen zu reduzieren, da es eine Compiler-Grenze für die Tiefe rekursiver Instanziierungen geben kann , und die Reduzierung der Gesamtzahl könnte die Kompilierungszeiten verkürzen.

    – Aschepler

    5. April 18 um 12:56 Uhr

  • Danke schön! Das ist großartig. Genau das, wonach ich gesucht habe.

    – Benutzer3613174

    5. April 18 um 13:04 Uhr

  • @aschepler – Ich weiß, ich weiß … Normalerweise verwende ich (in C ++ 11) eine andere Implementierung mit logarithmischer Komplexität. Aber meine Absicht war es, es einfach zu verstehen. Vielleicht versuche ich diesen Punkt etwas besser zu erklären…

    – max66

    5. April 18 um 13:13 Uhr

  • @ max66 Ja, diese einfache Erklärung ist definitiv besser für die Frage geeignet. Ich schlage nur ein paar zusätzliche Informationen vor.

    – Aschepler

    5. April 18 um 13:20 Uhr

  • @ user3613174 – ein paar Klarstellungen hinzugefügt (siehe auch den Kommentar des Ascheplers).

    – max66

    5. April 18 um 13:36 Uhr

1642851608 776 Details zu stdmake index sequence und
Papageien

Der Vollständigkeit halber füge ich eine modernere Implementierung von hinzu std::make_index_sequence, verwenden if constexpr und auto, die die Template-Programmierung viel mehr zur “normalen” Programmierung machen.

template <std::size_t... Ns>
struct index_sequence {};

template <std::size_t N, std::size_t... Is>
auto make_index_sequence_impl() {
    // only one branch is considered. The other may be ill-formed
    if constexpr (N == 0) return index_sequence<Is...>(); // end case
    else return make_index_sequence_impl<N-1, N-1, Is...>(); // recursion
}

template <std::size_t N>
using make_index_sequence = std::decay_t<decltype(make_index_sequence_impl<N>())>;

Ich empfehle dringend, diesen Stil der Vorlagenprogrammierung zu verwenden, da er einfacher zu begründen ist.

  • Interessant… ja, mit if constexpr einfacher werden. Aber das solltest du präzisieren if constexpr ist ab C++17 verfügbar.

    – max66

    5. April 18 um 13:25 Uhr

1642851608 462 Details zu stdmake index sequence und
Benutzer1095108

Damit es nicht vergessen wird:

template <std::size_t N, std::size_t ...I>
constexpr auto make_index_sequence_impl() noexcept
{
  if constexpr (!N)
  {
    return std::index_sequence<I...>();
  }
  else if constexpr (!sizeof...(I))
  {
    return make_index_sequence_impl<N - 1, 0>();
  }
  else if constexpr (N >= sizeof...(I))
  {
    return make_index_sequence_impl<N - sizeof...(I), I..., sizeof...(I) + I...>();
  }
  else
  {
    return []<auto ...J>(std::index_sequence<J...>) noexcept
    {
      return std::index_sequence<I..., sizeof...(I) + J...>();
    }(make_index_sequence_impl<N>()); // index concatenation
  }
}

template <size_t N>
using make_index_sequence = decltype(make_index_sequence_impl<N>());

.

589410cookie-checkDetails zu std::make_index_sequence und std::index_sequence

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy