2013-03-23 5 views
8

Betrachten Sie die folgende variadische FunktionForce all && ausgeführt werden?

template <typename Type, typename... Types> 
bool f(Type& arg, Types&... args) 
{ 
    return f(arg) && f(args...); 
} 

template <typename Type> 
bool f(Type& arg) 
{ 
    // Do something 
} 

Wenn eine Ebene der Rekursion false ist, dann vermute ich, dass die folgenden nicht ausgeführt werden. Gibt es einen Trick, um die Rekursion für alle Argumente zu erzwingen, selbst wenn einer von ihnen false zurückgibt?

+1

Wie über das Ersetzen '&&' mit '&'? – fredoverflow

Antwort

21

Dies sollte nicht zu hart sein:

template <typename Type, typename... Types> 
bool f(Type& arg, Types&... args) 
{ 
    bool b1 = f(arg); 
    bool b2 = f(args...); 
    return b1 && b2; 
} 
+0

Ich denke, nur die zweiten Aufrufe von 'f' loswerden. –

+0

@sftrabbit: Rest :) Vielen Dank –

+0

Siehe meine Antwort für den Benchmark. – Vincent

4

Sie diese separat ausführen kann und einen Bool Ausdruck zurück:

bool b0 = f(arg); 
bool b1 = f(args); 
return b0 && b1; 
4

Ohne Rekursion:

template <typename... Types> 
bool f(Types&&... args) 
{ 
    bool r=true; 
    (void)std::initializer_list<bool>{(r = f(args)&&r)...}; 
    return r; 
} 
-1

Es gibt eine viel schöner Trick, anstelle von & & zwischen allen Funktionen, verwenden Sie nur ein &

static_cast<bool>(f(arg)) & static_cast<bool>(f2(args)) ... werden alle Vorgänge unabhängig vom Ergebnis laufen :)

+0

Es sei denn, sie geben "int" statt "bool" zurück und die Werte sind "1" und "2" und Sie erhalten plötzlich "false" anstelle von "true". Entschuldigung, -1. –

+1

Dies kann mit 'static_cast ' behoben werden. Ich mag diesen Ansatz, da er kompakter ist als die mit Hilfsvariablen. – ipc

+0

Ich mag es auch, Casting Int zu Boolean ist einfach, und es ist schön und kompakt :), Schlechte Tricks ergeben solche negativen Stimmen: D, @Ipc edited durch Kommentar – Alon

11

Da die Debatte in einem Vergleich der AndyProwl und Alon Lösung entwickelt hat, ich Ich habe die beiden Lösungen verglichen, und das Ergebnis hängt von der Anzahl der Argumente ab.

Kompilieren mit:

g++-4.7 -std=c++11 -Wall -Wextra -O3 main.cpp -o main -D_FIRST 

Benchmarks die AndyProwl Lösung und Kompilieren mit:

g++-4.7 -std=c++11 -Wall -Wextra -O3 main.cpp -o main -D_SECOND 

Benchmarks die Lösung Alon.

Hier ist das Programm des Benchmarks für 10 Argumente.

#include <iostream> 
#include <chrono> 

// Function 1 : with && 
template <typename Type> 
inline bool f1(const Type& arg) 
{ 
    return arg; 
} 
template <typename Type, typename... Types> 
inline bool f1(const Type& arg, const Types&... args) 
{ 
    bool arg1 = f1(arg); 
    bool arg2 = f1(args...); 
    return arg1 && arg2; 
} 

// Function 2 : with & 
template <typename Type> 
inline bool f2(const Type& arg) 
{ 
    return arg; 
} 
template <typename Type, typename... Types> 
inline bool f2(const Type& arg, const Types&... args) 
{ 
    return f2(arg) & f2(args...); 
} 

// Benchmark 
int main(int argc, char* argv[]) 
{ 
    // Variables 
    static const unsigned long long int primes[10] = {11, 13, 17, 19, 23, 29, 31, 37, 41, 43}; 
    static const unsigned long long int nbenchs = 50; 
    static const unsigned long long int ntests = 10000000; 
    unsigned long long int sum = 0; 
    double result = 0; 
    double mean = 0; 
    std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now(); 

    // Loop of benchmarks 
    for (unsigned long long int ibench = 0; ibench < nbenchs; ++ibench) { 

     // Initialization 
     t0 = std::chrono::high_resolution_clock::now(); 
     sum = 0; 

     // Loop of tests 
     for (unsigned long long int itest = 1; itest <= ntests; ++itest) { 
#ifdef _FIRST 
      sum += f1((itest+sum)%primes[0], (itest+sum)%primes[1], (itest+sum)%primes[2], (itest+sum)%primes[3], (itest+sum)%primes[4], (itest+sum)%primes[5], (itest+sum)%primes[6], (itest+sum)%primes[7], (itest+sum)%primes[8], (itest+sum)%primes[9]); 
#endif 
#ifdef _SECOND 
      sum += f2((itest+sum)%primes[0], (itest+sum)%primes[1], (itest+sum)%primes[2], (itest+sum)%primes[3], (itest+sum)%primes[4], (itest+sum)%primes[5], (itest+sum)%primes[6], (itest+sum)%primes[7], (itest+sum)%primes[8], (itest+sum)%primes[9]); 
#endif 
     } 

     // Finalization 
     result = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now()-t0).count(); 
     mean += result; 
     std::cout<<"time = "<<result<<" (sum = "<<sum<<")"<<std::endl; 
    } 

    // End 
    std::cout<<"mean time = "<<mean/nbenchs<<std::endl; 
    return 0; 
} 

Mit 50 Benchmarks für jede Lösung mit einer bestimmten Anzahl von Argumenten ist die Dispersion sehr klein, und die mittlere Zeit über dieses Benchmarks ist ein zuverlässiger Indikator.

Meine erste Benchmark war mit der "richtigen" Anzahl von Argumenten, wo die Alon-Lösung schneller ist als die AndyProwl-Lösung.

Die endgültigen Ergebnisse sind hier:

Benchmark

So ist die AndyProwl Lösung in der Regel schneller als die Alon ist. So, jetzt kann ich deine Antwort bestätigen. Aber ich denke, dass der Unterschied so klein ist, dass es von Architektur/Compiler abhängig ist.

So:

  • AndyProwl + 1 für die im Allgemeinen schnellere Lösung
  • Alon + 1 für Ihre constexpr fertige Lösung
+2

Nettes Diagramm, danke für das Teilen der Informationen. +1 –

+0

Alle Grafiken benötigen Fehlerbalken – Inverse

+0

Da ich den Code veröffentlicht habe, können Sie es mehrmals ausführen und die Dispersion berechnen. – Vincent