2016-06-21 18 views
0

Ich möchte eine Template-Funktion foo mit einer Sequenz verschiedener (nicht typisierter) Parameter aufrufen, wobei aufeinanderfolgende ganzzahlige Werte zwischen einer unteren und oberen Grenze genommen werden. Zum Beispiel:Aufruf der Template-Funktion mit mehreren Parameterwerten

template <int K> void foo(){ ... } 

int const UPPER = 10, LOWER = 5; 

for(int i = LOWER; i <= UPPER; i++) 
    foo<i>(); 

Das ist natürlich, wird nicht funktionieren, da i wird bei der Kompilierung nicht bekannt. Ich bin auf der Suche nach einer Möglichkeit, diese Art von Programm zu erreichen, ohne so etwas zu schreiben:

foo<5>(); foo<6>(); foo<7>(); foo<8>(); foo<9>(); foo<10>(); 

Dies ist vor allem, weil ich auf der Veränderung UPPER und LOWER von einer Ausführung zum nächsten beabsichtigen.

Meine einzige Idee war, eine konstante Array der ganzen Zahlen zu erzeugen, die an die Template-Parameter wird gesendet:

int const arr[6] = {5, 6, 7, 8, 9, 10}; 

for(int i = LOWER; i <= UPPER; i++) 
    foo<arr[i]>(); 

Aber noch einmal, obwohl die Elemente des Arrays konstant sind, ist i bei nicht bekannt Kompilierzeit so ist weder arr[i]. Irgendwelche Vorschläge?

Vielen Dank im Voraus.

+2

Es sieht aus wie Sie [ 'std verwenden möchten: : integer_sequence'] (http://en.cppreference.com/w/cpp/utility/integer_sequence) – NathanOliver

+0

Sie könnten foo() in eine 'template struct foowrapper' schreiben, das macht es viel einfacher. Ist das erlaubt ? – lorro

Antwort

3

Sie können zwei Vorlagen und std::enable_if verwenden, um eine davon auszuwählen, je nachdem, ob Lower gleich Upper ist. Wenn sie gleich sind, tun wir nichts. Andernfalls rufen wir foo<Lower>() auf und rekursiv mit den Parametern Lower + 1 und Upper.

template <int Lower, int Upper> 
typename std::enable_if<Lower == Upper, void>::type callFoo() 
{ 

} 

template <int Lower, int Upper> 
typename std::enable_if<Lower != Upper, void>::type callFoo() 
{ 
    static_assert(Lower < Upper, "Lower must be less than or equal to Upper"); 

    foo<Lower>(); 
    callFoo<Lower + 1, Upper>(); 
} 

diese Vorlage gegeben, wird die folgende Zeile foo<K>() für K Werte 5, rufen 6, 7, 8, 9, 10.

callFoo<5, 11>(); 
+0

Sehr elegante Lösung. Vielen Dank. – tmnol

+0

Aus Neugier wissen Sie von Workarounds, die diesen Code mit älteren Versionen von C++ (vor C++ 11) kompatibel machen könnten? – tmnol

0

Soweit ich weiß Vorlagen werden während der Kompilierung in tatsächliche Strukturen aufgelöst, so dass Sie int als Argument übergeben müssen, um zu funktionieren.

+1

Die Sache ist, dass das OP die ganzen Zahlen zur Kompilierzeit kennt. Es ist eine Frage des Entsprechenden einer Kompilierzeitschleife, die sie als Vorlagenargumente verwenden kann. – chris

+0

Ich weiß, aber ich wusste nicht, dass so etwas existiert. @NathanOliver zeigte auf etwas, von dem ich nichts wusste, std :: integer_sequence – MaciekGrynda

+0

@MaciekGrynda Wenn ich die ganzen Zahlen als Argumente für die Funktion übergeben könnte, würde es mein Leben tatsächlich viel einfacher machen. Aber ich kann nicht, aus Gründen, die den Rahmen der Frage überschreiten. – tmnol

3

Sie können von std::integer_sequence machen einen Compiler-Liste von Zahlen von 0 aufsteigend zu bekommen und dann fügen Sie Ihre Offset:

// Here we take the lower bound and the sequence 0 to (Upper - Lower). 
// We call foo with each number in the sequence added to the lower bound. 
template<int Lower, int... Ints> 
void call_foo_with_range_helper(std::integer_sequence<int, Ints...>) { 
    // A common trick to expand the parameter pack without recursion or fold expressions 
    (void)std::initializer_list<int>{(foo<Lower + Ints>(), 0)...}; 
} 

// This simply makes it easier for the caller to use. 
// We take the lower and upper bounds only. 
template<int Lower, int Upper> 
void call_foo_with_range() { 
    call_foo_with_range_helper<Lower>(std::make_integer_sequence<int, Upper - Lower + 1>()); 
} 

int main() { 
    int const UPPER = 10, LOWER = 5; 

    call_foo_with_range<LOWER, UPPER>(); 
} 
+0

Vielen Dank für Ihre Antwort.Ich habe die Antwort von @TerraPass als am besten markiert, da ich es leichter hatte, sie zu verstehen (und ich nehme an, dass die Leute meinen Code auch lesen), aber beide sind gute Lösungen. – tmnol

+2

@TudorManne, Kein Problem. Das gesamte 'initializer_list'-Geschäft ist unglücklich und sobald falte Ausdrücke verfügbar sind, kann es durch' (foo (), ...); 'ersetzt werden. Abgesehen davon ist 'std :: integer_sequence' eines der Dinge, die Sie ein paar Mal brauchen, bevor Sie es wirklich schätzen. In diesem Fall unterstützt der Großteil der Arbeit jeden Bereich und nicht einen, der bei 0 beginnt. – chris