2016-07-19 340 views
6

Supponiamo che ci sia una funzione con un valore di default:Come gestire i valori di default nelle chiamate di funzione (profondamente) nidificate?

int foo(int x=42); 

Se questo è chiamato da altri in questo modo:

int bar(int x=42) { return foo(x); } 
int moo(int x=42) { return bar(x); } 

Questo è naturalmente solo un esempio forzato. Tuttavia, a volte ho una situazione abbastanza simile. Il parametro viene passato dal livello più alto (moo) al valore più basso e solo lì viene effettivamente utilizzato. La cosa brutta è che quando cambio foo con un valore predefinito diverso da 42, dovrei cercare tutti i chiamanti e modificare di conseguenza il valore predefinito.

C'è qualche schema/linguaggio per evitare questa situazione?

L'unica soluzione semplice che mi viene in mente è

int bar()  { return foo(); } 
int bar(int x) { return foo(x); } 

Tuttavia, come io sono un po 'pigro e nel codice reale questo porterebbe a un po' di duplicazione del codice, vorrei evitare questo.

+0

Direi che non dovresti utilizzare il valore predefinito del parametro per i valori predefiniti arbitrari. Per quelli vorrei usare variabili const con namespace. I valori predefiniti dei parametri sono validi per cose come i valori di inizializzazione * sane * (zero/nullptr/false). – Galik

risposta

2

Le soluzioni generali pratiche includono:

  • utilizza una classe Optional_ per l'argomento, come ad esempio boost::optional o un equivalente fai da te.

  • Assegna un nome al valore predefinito (e utilizza il nome nelle definizioni della funzione wrapper).

  • Sovraccaricare ogni funzione wrapper, come indicato nella domanda.

  • Basta ripetere il valore predefinito nelle definizioni di funzione wrapper, ma ciò interrompe il principio di DRY, non ripetersi.


In un comment else-thread Tobi porta in primo piano il caso di un wrapper asdf definito come

int asdf(int x=42,int y=42){ return foo(x)+foo(y);} 

Utilizzando un Optional_ classe:

auto foo(Optional_<int> x) 
    -> int 
{ return (x.is_empty()? 42 : x.value()); } 

auto asdf(Optional_<int> x = {}, Optional_<int> y = {}) 
    -> int 
{ return foo(x) + foo(y); } 

Utilizzando un valore predefinito denominato:

int const foo_default = 42; 

auto foo(int x = foo_default) 
    -> int 
{ return x; } 

auto asdf(int x = foo_default, int y = foo_default) 
    -> int 
{ return foo(x) + foo(y); } 

Uso sovraccarichi:

auto foo(int x = 42) 
    -> int 
{ return x; } 

auto asdf() 
    -> int 
{ return foo() + foo(); } 

auto asdf(int x) 
    -> int 
{ return foo(x) + foo(); } 

auto asdf(int x, int y) 
    -> int 
{ return foo(x) + foo(y); } 

Vale la pena notare che asdf non può essere facilmente definito come un modello di funzione di inoltro suoi argomenti. Inoltre, un modello di questo tipo non può essere facilmente definito in un'unità di traduzione separata e uno non può prendere il suo indirizzo. Per questi motivi non ho incluso questa possibile soluzione nella lista dei proiettili: è molto vincolata, non una soluzione generale.

+0

'boost :: opzionale' * o *' std :: optional' ora. – lorro

+0

Non sono sicuro del motivo per cui non mi sono reso conto dell'opzione di una costante denominata anch'io ... a volte sto solo pensando troppo complicato (anche per C++) – user463035818

6

Consiglierei di selezionare una delle seguenti due opzioni (come puoi vedere in altre risposte - ci sono più soluzioni possibili).

  1. sovraccarico le funzioni
  2. Definire costante

Così, l'opzione 1 sarebbe aspetto come di seguito:

int foo(int x=42); 
int bar(int x) { return foo(x); } 
int moo(int x) { return bar(x); } 
int bar() { return foo(); } 
int moo() { return bar(); } 

E, l'opzione 2 sarà un po 'più corto:

constexpr int FOO_DEFAULT = 42; 
int foo(int x=FOO_DEFAULT); 
int bar(int x=FOO_DEFAULT) { return foo(x); } 
int moo(int x=FOO_DEFAULT) { return bar(x); } 

Vorrei usare o ption-1 per i casi con numero ridotto di valori di default (come un valore di default), l'opzione-2 per i casi in cui si ha un bel paio di valori di default - come foo(int a, int b = 3, std::string c = "wow", float pi = 3.14)

+0

Aumento di voti nonostante "Sono disponibili due opzioni", che non è corretto. :) –

+0

Considerato, rimosso l'upvote dovuto agli identificativi TUTTI I MAIUSCOLI (non ho notato, scusa). È probabile che tutti i caratteri maiuscoli siano in conflitto con i nomi macro. Quindi questo insegna un idioma un po 'C++: è OK in Java, e necessario in Python, ma molto buono in C++. –

+0

@ Cheersandhth.-Alf Credo OP se uso la mia soluzione proposta sceglierà il nome in base al suo standard di codifica/stile (se ne hai). A proposito, la mia opinione su questo argomento molto specifico è che lo stesso stile dovrebbe essere usato per macro e variabili const variabili - perché logicamente - ci sono la stessa cosa, sono usati per raggiungere lo stesso obiettivo. Quindi questa è un'opinione opposta al tuo ... – PiotrNycz

3

si può evitare la duplicazione con:

template<typename... T> 
    auto bar(T&&... t) { return foo(std::forward<T>(t)...); } 

Ma questo non è un miglioramento IMHO. Basta smettere di essere pigri e definire i sovraccarichi che chiamano foo() quando non viene fornito alcun argomento.

+1

** - 1 ** Cambia la natura di 'bar'. Ora non si può prendere il suo indirizzo. Non è possibile (praticamente) implementarlo in un'unità compilata separatamente. Chiamandolo con un buon numero di argomenti o tipi di argomenti, si ottengono errori di istanziazione del modello anziché semplici diagnosi. –

+1

Come ho già detto, non è un miglioramento. Ma ** fa ** evita la duplicazione, che è ciò che l'OP voleva evitare. –

+1

Penso che non funzionerà quando ho eg 'int asdf (int x = 42, int y = 42) {return foo (x) + foo (y);}' – user463035818