2017-04-04 1 views
7

Ich möchte so etwas schreiben:Iterate mit constexpr

template<int i> void f() {} 

for (constexpr int i : {1,2,3}) 
{ 
    f<i>(); 
} 

Ist es möglich, auf constexpr iterieren?

Danke

Antwort

5

Wie Sie wahrscheinlich verstehen, können Sie das nicht tun, wie von:

for (constexpr int i : {1,2,3}) 
{ 
    f<i>(); 
} 

denn wenn i eine Zahl von 1 bis 3 in einer Schleife variieren, dann ist es eine Variable und kein compiletime Konstante. Und eine Variable kann kein Template-Argument sein, wie in f<i>: nur eine Compiletime-Konstante kann ein Template-Argument sein.

In C++ 11 und später dank variadic templates, Sie können effektiv eine willkürliche Folge von compiletime iterieren Konstanten durch compiletime Rekursion einer Template-Funktion verwenden, das eine geeignete beliebige Reihenfolge von Template-Argumenten akzeptiert.

Das wird Ihnen kaum etwas bedeuten, wenn Sie nicht bereits wissen, wie es geht. Hier ist ein C 11 ++ Beispiel das tut, was Sie wollen zum Ausdruck bringen:

#include <type_traits> 
#include <iostream> 

template<int i> void f() 
{ 
    std::cout << i << '\n'; 
} 

// This overload is chosen when there is only 1 template argument. 
template<int First, int ...Rest> 
typename std::enable_if<sizeof...(Rest) == 0>::type 
for_each_f() 
{ 
    f<First>(); 
} 

// This overload is chosen when there is > 1 template argument. 
template<int First, int ...Rest> 
typename std::enable_if<sizeof...(Rest) != 0>::type 
for_each_f() 
{ 
    f<First>(); 
    for_each_f<Rest...>(); 
} 

int main() 
{ 
    for_each_f<2,3,5,7,11>(); 
    return 0; 
} 

See it live

Neben variadische Vorlagen hängt diese Technik auf dem sehr wichtigen C++ Meta-Programmierung Prinzip der SFINAE und auf std::enable_if, das ist das Werkzeug, das die Standard-C++ - Bibliothek zum Ausnutzen von SFINAE bietet.

101010 Antwort zeigt einen differenzierteres und kraftvollen Stil von Lösung, die in C++ 14 (und leicht genug, um implementieren, in C++ 11 , wenn Sie etwas Unterstützung vorformulierten schreiben) zur Verfügung steht.

+0

TBH Ich bevorzuge diesen Ansatz. Um diese Initialisierung von 'int t []' (C++ 's Fehler, nicht 101010) herum gibt es so viel schrecklichen Hacker, dass mir eine Menge Geld gegeben werden müsste, bevor ich diesen Weg gehen würde! –

+0

können Sie die 'enable_if's loswerden, wenn Sie die Template-Signaturen in' template for_each_f(); 'und' template for_each_f(); 'ändern –

5

Nein, Sie nicht ein for-Schleife zu durchlaufen bei der Kompilierung verwenden können. Die für die Kontrollstruktur in C++ wird für den Laufzeitkontrollfluss verwendet.

Sie können jedoch auch andere Kompilierzeitfunktionen verwenden. Zum Beispiel in C++ 14 könnten Sie erreichen, was Sie in der folgenden Art und Weise wollen:

  1. eine Vorlage-Wrapper-Klasse definieren, die Ihre Funktion geht zu nennen ist.

    template<int i> 
    struct wrapper { 
        void operator()() const { f<i>(); } 
    }; 
    
  2. Verwenden std::index_sequence erzeugen Zeitindizes kompilieren.

    template<template<int> class W, std::size_t... I> 
    void caller_impl(std::index_sequence<I...>) { 
        int t[] = { 0, ((void)W<I>()(), 1)... }; 
        (void) t; 
    } 
    
    template<template<int> class W, std::size_t N, typename Indices = std::make_index_sequence<N>> 
    void call_times() { 
        caller_impl<W>(Indices()); 
    } 
    
  3. Dann rufen als

    int main() { 
        call_times<wrapper, 42>(); 
    } 
    

    Live Demo

Wenn C++ 14 keine Option ist, dass Sie einen Blick darauf werfen konnte here von, wie Sie std::index_sequence Ihr Selbst implementieren könnten.

+0

Was ist '(void) t' für? –

+1

@DeanSeo unterdrückt Compiler-Warnung für nicht verwendete Variable. – 101010

+0

Wenn Sie C++ 17 haben, dann kann der 'caller_impl' Funktionskörper sein' (W ()(), ...); 'Viel einfacher zu verstehen. –