2016-06-15 14 views
11

Avevo scritto funzioni di ordine superiore come questo:oggetti perfetti-spedizione richiamabili in funzioni di ordine superiore

template<typename F, typename... Args> 
void doStuff(F f, Args&&... args) 
{ 
    // ... 
    f(std::forward<Args>(args)...); 
    // ... 
} 

O forse sostituire F f con F&& f.

Ma dopo aver saputo delle qualifiche di ref (ahi), le cose si sono complicate. Immaginate una classe funtore:

struct Foo { 
    void operator()(...) &; 
    void operator()(...) &&; 
}; 

Poi i miei precedenti implementazioni di doStuff sarà sempre e solo chiamare il metodo &, dal momento che i parametri sono sempre lvalue.

Penso che il modo per risolvere questo è quello di implementare doStuff in questo modo:

template<typename F, typename... Args> 
void doStuff(F&& f, Args&&... args) 
{ 
    // ... 
    std::forward<F>(f)(std::forward<Args>(args)...); 
    // ... 
} 

Questo è anche il modo std::result_of è possibly implemented. Quello che voglio sapere è che c'è qualche svantaggio in questa implementazione, cioè dovrei sostituire tutte le mie implementazioni HOF con esso?

+0

Sembra buono fintanto che 'f' non verrà riutilizzato dopo. È possibile che 'operator()' abbia alcuni effetti collaterali che possono modificare i membri interni (spostati sui membri interni) di 'f' (se esiste). Non sono sicuro, però, non ci ho pensato ... Buona domanda. – Arunmu

+0

@Arunmu Se (1) il metodo è sovraccarico su qualificatori ref e (2) il chiamante passa il functor come valore di rvalore, abbiamo una ragione sufficiente per credere che il chiamante intenda chiamare la versione '&&'. Stando così le cose, è probabilmente ragionevole perfezionare ogni occorrenza di 'f' in' doStuff' ... penso. –

+1

Non vedo alcun problema con 'forward (f)' purché non si usi 'f' di nuovo all'interno di' doStuff' – Praetorian

risposta

0

std::forward è un cast di valore r condizionale e il punto è come al solito per rubare il coraggio dagli oggetti morenti. Non c'è differenza tra functors e altri oggetti in questo senso. Il lato negativo è identico a qualsiasi altro utilizzo di std::forward; se non sei pronto a rubare quelle budella, non dovresti.

#include <iostream> 
#include <vector> 

struct Foo { 
    std::vector<int> vec = {1,2,3}; 
    std::vector<int>& operator()(...) & { 
     return vec; 
    } 
    std::vector<int> operator()(...) && { 
     return std::move(vec); 
     // Clearly, don't rely on 'vec' here... 
    } 
}; 

Foo getFoo(){ 
    return {}; 
} 

int main() { 
    auto foo = getFoo(); 
    auto vec1=foo(); 
    auto vec2=getFoo()(); 
} 

Chiaramente, non fare cose del genere:

auto vec3 = std::move(foo)(); 
vec4 = foo(); 

per le stesse ragioni.

Problemi correlati