2015-08-16 9 views
7

Per esempio, perché non riesco a scrivere questo:Perché non posso usare i valori degli argomenti precedenti per definire i valori di default degli argomenti?

void f(double x, double y = x); 

per dichiarare una funzione f, per il quale la chiamata f(x) è equivalente a f(x,x)?

Nel caso in cui questo non ti sembra utile, ecco un possibile scenario di utilizzo. In questo esempio, dichiaro f come segue:

void f(double x, double y = expensiveComputation(x)); 

dove expensiveComputation denota, avete indovinato, una funzione che esegue un calcolo molto lento. Voglio dare all'utente di f la possibilità di passare il valore di se lo ha già calcolato in precedenza, quindi non devo computarlo di nuovo all'interno di f. Ora, naturalmente posso anche risolvere questo scrivendo due overload:

void f(double x, double y); 
void f(double x) { f(x, expensiveComputation(x)); } 

ma scrivere sovraccarichi diventa faticoso come il numero di argomenti cresce. Ad esempio, prova a scrivere:

void f(double x, double p = expensiveComputation(x), 
       double q = expensiveComputation2(x, p), 
       double r = expensiveComputation3(x, p, q), 
       double s = expensiveComputation3(x, p, q, r)); 

utilizzando sovraccarichi. È solo più brutto. Gli argomenti predefiniti sono sexy. Esiste un motivo sintattico più profondo per cui gli argomenti precedenti non possono essere utilizzati per definire i valori predefiniti degli argomenti?

+3

Consentire questa sintassi forzerebbe l'implementazione a valutare gli argomenti da sinistra a destra? – Brian

+0

@Brian Buon punto. Perché l'implementazione non è obbligata a valutare gli argomenti da sinistra a destra? Dimenticavo ... – becko

+0

Su molte piattaforme la convenzione di chiamata predefinita è quella di spingere gli argomenti nello stack a partire da quello più a destra e procedendo verso sinistra. (Il callee viene quindi visualizzato da sinistra a destra.) – Brian

risposta

0

È possibile utilizzare i modelli variadic per qualcosa di simile:

template<typename... Args> void f(double x, Args... args) 
{ 
    typedef typename common_type<double, Args...>::type common; 
    std::vector<common, sizeof...(args)> arguments = {{ args... }}; 

    if (arguments.size < 2) arguments.push_back(expensiveComputation(arguments[0])); 
    if (arguments.size < 3) arguments.push_back(expensiveComputation2(arguments[0], arguments[1])); 
    if (arguments.size < 4) arguments.push_back(expensiveComputation3(arguments[0], arguments[1], arguments[2])); 
    if (arguments.size < 5) arguments.push_back(expensiveComputation4(arguments[0], arguments[1], arguments[2], arguments[3])); 
} 
1

Non so il motivo per cui argomento di default non può andare in quel modo, ma forse si può provare di avvolgere tutti questi argomenti in una struttura (classe)?

struct ParamSet 
{ 
    double x; 
    double p; 
    double q; 

    ParamSet(double x) 
     : x(x) 
     , p(ExpensiveCompute1(x)) 
     , q(ExpensiveCompute2(x, p)) 
    { 
    } 
    ParamSet(double x, double p) 
     : x(x) 
     , p(p) 
     , q(ExpensiveCompute2(x, p)) 
    { 
    } 
    ParamSet(double x, double p, double q) 
     : x(x), p(p), q(q) 
    { 
    } 

private: 
    double ExpensiveCompute1(double x) {} 
    double ExpensiveCompute2(double x, double p) {} 
};  
void f(ParamSet ps); 

Ancora bisogno di sovraccarichi ctor, ma non più opere di scrivere la serie expensiveComputation() come hai fornito, e almeno tutte le cose sono avvolti nella struct

Inoltre, la firma di f() potrebbe essere fissato, e non vi è solo una versione.

Problemi correlati