2013-03-23 15 views
8

Rozważmy następującą funkcję zmiennej liczbie argumentówWymuszenie wszystkich && do wykonania?

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 
} 

Jeśli jeden poziom rekurencji jest false, to podejrzewam, że następujące nie zostanie wykonane. Czy istnieje sztuczka wymuszająca rekursję na wszystkich argumentach, nawet jeśli jedna z nich zwróci false?

+1

Jak o wymianie '' && z '&'? – fredoverflow

Odpowiedz

21

To nie powinno być zbyt trudne:

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

Myślę, że po prostu pozbywam się drugiego połączenia z 'f'. –

+0

@ftopbit: Pozostawione :) Dziękuję –

+0

Zobacz moją odpowiedź na benchmark. – Vincent

4

Można wykonać je osobno i powrócić wyrażenie bool:

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

Bez rekursji:

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

Jest o wiele przyjemniej, zamiast używać & & pomiędzy wszystkimi funkcjami, wystarczy użyć jeden &

static_cast<bool>(f(arg)) & static_cast<bool>(f2(args)) ... wszystkie operacje będą działać niezależnie od wyniku :)

+0

Chyba, że ​​zwracają 'int' zamiast' bool', a wartości to '1' i' 2' i nagle dostajesz 'false' zamiast' true'. Przepraszam, -1. –

+1

Można to naprawić za pomocą 'static_cast '. Podoba mi się to podejście, ponieważ jest bardziej kompaktowy niż te ze zmiennymi pomocniczymi. – ipc

+0

Ja też to lubię, rzucanie int do boolean jest łatwe, i jest ładne i kompaktowe :), zbyt złe sztuczki dają takie negatywne głosy: D, @Ipc edytowane z powodu komentarza – Alon

11

Ponieważ dyskusja rozwinęła się w porównaniu z roztworem AndyProwl i Alon, I” ve porównało oba rozwiązania, a wynik ... zależy od liczby argumentów.

Kompilacja z:

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

benchmarków roztworze AndyProwl i zestawiania z:

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

benchmarków roztworze Alon.

Oto program testu porównawczego dla 10 argumentów.

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

Z 50 wskaźników dla każdego roztworu o określonej liczbie argumentów, dyspersja jest bardzo mała, a średni czas w ciągu tych wzorców jest wiarygodnym wskaźnikiem.

Mój pierwszy benchmark był z "właściwą" liczbą argumentów, gdzie rozwiązanie Alon jest szybsze niż rozwiązanie AndyProwl.

Ostateczne wyniki są tutaj:

Benchmark

więc rozwiązanie AndyProwl jest na ogół szybciej niż jednego Alon. Teraz mogę zweryfikować twoją odpowiedź. Ale myślę, że różnica jest tak mała, że ​​zależy ona od architektury/kompilatora.

Więc:

  • AndyProwl + 1 za generalnie szybszego rozwiązania
  • Alon + 1 dla constexpr gotowe rozwiązania
+2

Ładny wykres, dziękuję za udostępnienie informacji. +1 –

+0

Wszystkie wykresy wymagają pasków błędów – Inverse

+0

Po opublikowaniu kodu można uruchomić go kilka razy i obliczyć dyspersję. – Vincent

Powiązane problemy