2013-03-23 11 views
8

Si consideri la seguente funzione variadicForza tutto && per essere eseguito?

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 
} 

Se un livello di ricorsione è false, quindi ho il sospetto che non sarà eseguito quanto segue. C'è un trucco per forzare la ricorsione su tutti gli argomenti anche se uno di questi restituisce false?

+1

ne dite di sostituire '&&' con '&'? – fredoverflow

risposta

21

Questo non dovrebbe essere troppo difficile:

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

Penso di sbarazzarmi delle seconde chiamate a 'f'. –

+0

@sftrabbit: Leftover :) Grazie –

+0

Vedere la mia risposta per il punto di riferimento. – Vincent

4

Si possono eseguire separatamente e restituire un'espressione bool:

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

Senza ricorsione:

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

C'è una trucco molto più bello, invece di usare & & tra tutte le funzioni, basta usare un &

static_cast<bool>(f(arg)) & static_cast<bool>(f2(args)) ... verrà eseguito tutte le operazioni indipendentemente dal risultato :)

+0

A meno che non restituiscano 'int' invece di' bool' ei valori siano '1' e' 2' e improvvisamente si ottiene 'false' invece di' true'. Siamo spiacenti, -1. –

+1

Questo può essere risolto con 'static_cast '. Mi piace questo approccio poiché è più compatto rispetto a quelli con variabili helper. – ipc

+0

Mi piace anche a me, lanciare int a booleano è facile, ed è bello e compatto :), trucchi troppo brutti producono voti negativi: D, @Ipc editted a causa del commento – Alon

11

Mentre il dibattito si è evoluto in un confronto della soluzione AndyProwl e Alon, I' Ho confrontato le due soluzioni e il risultato ... dipende dal numero di argomenti.

compilazione con:

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

parametri di riferimento la soluzione AndyProwl e la compilazione con:

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

parametri di riferimento la soluzione Alon.

Ecco il programma del benchmark per 10 argomenti.

#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; 
} 

Con 50 parametri per ogni soluzione con un determinato numero di argomenti, la dispersione è molto piccola, e il tempo medio su questi parametri è un indicatore affidabile.

Il mio primo punto di riferimento è stato con il "giusto" numero di argomenti in cui la soluzione Alon è più veloce della soluzione AndyProwl.

I risultati finali sono qui:

Benchmark

Quindi la soluzione AndyProwl è generalmente più veloce di quello Alon. Quindi, ora posso convalidare la tua risposta. Ma penso che la differenza sia così piccola che dipende dall'architettura/dal compilatore.

Quindi:

  • AndyProwl + 1 per la soluzione generalmente più veloce
  • Alon + 1 per la soluzione constexpr-ready
+2

Bel grafico, grazie per aver condiviso le informazioni. +1 –

+0

Tutti i grafici necessitano di barre di errore – Inverse

+0

Come ho postato il codice, è possibile eseguirlo più volte e calcolare la dispersione. – Vincent