Restituisce un indicatore se il processo ha esito positivo o meno

5

Ho una funzione membro che assomiglia a:

Point find_the_special_point(Image img);

Tuttavia, questa funzione potrebbe non riuscire a trovare il punto (ad esempio img non contiene affatto un punto speciale ).

Qual è il modo giusto per far ritornare la funzione in qualche modo che il punto non è stato trovato?

Ho pensato ad alcune soluzioni ma non sono sicuro quale sia il migliore o se esiste un approccio migliore:

1. Eccezione di lancio se il punto non viene trovato:

Penso che sia semplicemente stupido perché le eccezioni non significano controllare il flusso. Inoltre, non trovare un punto è un comportamento previsto non un'eccezione.

2. Modificare la dichiarazione in:

Bool find_the_special_point(Image img , Point& pnt);

Anche se sembra corretto, ritengo (non sicuro) che non sia così bello restituire l'output effettivo tramite argomenti.

3. Disporre il motto:

struct find_the_special_point_result{
    bool is found;
    Point special_point;
};
find_the_special_point_result find_the_special_point(Image img);

Sembra una buona soluzione. Tuttavia, sospetto che sia un po 'eccessivo.

4. Restituito puntatore invece:

std::shared_ptr<Point> find_the_special_point(Image img);

e se non viene trovato restituisce nullptr . Tuttavia, penso che l'allocazione dinamica non sia stata fatta per questo caso. È come utilizzare lo strumento sbagliato per raggiungere un altro obiettivo.

Qual è il modo in cui la convenzione risolve questo problema?

    
posta Humam Helfawi 23.02.2018 - 14:46
fonte

4 risposte

4

In C ++, questa domanda non è banale a causa della vita degli oggetti e dei costruttori / distruttori. Non importa per i tipi di POD. Ma è rilevante per altri tipi e di fondamentale importanza quando si scrive codice basato su modelli.

Se si restituisce per valore (anche se racchiuso in una struttura o in una tupla), è sempre necessario creare un'istanza di questo oggetto. Questo potrebbe non essere fattibile per oggetti che acquisiscono una risorsa nel loro costruttore, o che non sono costruttibili in modo predefinito.

Se si restituisce per parametro di output, questo è ancora peggio. Il chiamante deve creare un'istanza di un oggetto vuoto ed è necessario copiare il valore di ritorno in quell'oggetto. Ciò significa che l'oggetto deve avere uno stato "vuoto" ed essere mutabile. Questo stato vuoto potrebbe essere in disaccordo con RAII e rende il codice più fragile.

Le tre soluzioni robuste sono:

  • usa le eccezioni,
  • utilizza un tipo optional<T> o
  • ritorno per puntatore.

Sono tutti molto buoni, ma hanno diversi compromessi.

Le eccezioni ti permettono di scrivere il tuo codice come se il valore di ritorno fosse sempre lì. Tuttavia, le eccezioni C ++ non sono adatte per un tipo di codice di ritorno. Usali solo se l'output sarà di solito lì, e la sua assenza è un errore di qualche tipo. Per esempio. std::vector::at() utilizza eccezioni quando nessun risultato può essere restituito perché un controllo dei limiti non è riuscito.

Un tipo facoltativo consente di restituire per valore, ma il valore potrebbe non essere inizializzato. Questo elude i problemi sopra menzionati relativi alla costruzione dell'oggetto restituito. Un tipo facoltativo può essere considerato come un unione con tag sicuro per tipo con un singolo membro.

Il ritorno da un tipo puntatore è la soluzione più generale, poiché può gestire oggetti che non possono essere copiati o spostati. Utilizzando puntatori intelligenti, è possibile specificare la semantica della proprietà (puntatori univoci, condivisi o semplici per non specificato / preso in prestito). Questo avviene molto comunemente con algoritmi basati su iteratori, poiché gli iteratori sono in qualche modo puntatori. Il vantaggio dell'uso di tipi con una conversione booleana è che puoi facilmente controllare il valore restituito in modo condizionale:

if (auto result = some_function()) {
  result->foo();  // statically known that result != nullptr
}

Puntatori o riferimenti sono comunque necessari quando si restituiscono i tipi polimorfici, quindi l'uso dei puntatori potrebbe non essere un grande cambiamento.

Lo svantaggio è ovviamente quando crei un nuovo oggetto che vuoi che sia il chiamante a possedere. Dovresti quindi restituire un unique_ptr . Ma se si restituirebbe in base al valore, restituire un optional sarebbe meglio, in quanto evita indirette non necessarie e allocazioni di heap.

    
risposta data 23.02.2018 - 23:30
fonte
3

Restituire qualcosa se quel qualcosa è lì, e null'altro, è esattamente ciò che% è fatto per% co_de.

Se il tuo compilatore non ha ancora questo tipo e non puoi aggiornare, puoi utilizzare la versione Boost.

    
risposta data 23.02.2018 - 16:26
fonte
1

Direi che l'opzione # 2 è la strada da percorrere. Come hai affermato nell'Opzione n. 1, le eccezioni non hanno lo scopo di controllare il flusso del programma; soprattutto se il fallimento è un'opzione prevista. (che è nel tuo caso)

I booleani sono, e sono usati frequentemente. vale a dire: if(flag) #do something else #do other thing

    
risposta data 23.02.2018 - 16:29
fonte
-7

1. Eccezione di lancio se il punto non viene trovato:

Questo è pericoloso perché le eccezioni sono goto sotto mentite spoglie. Prende esperienza con loro per farli correggere. Se stai scrivendo questo per gli altri, allora ti consiglio di evitare questa opzione.

2. Modificare la dichiarazione in:

Un'opzione valida, ma altri sviluppatori ignoreranno il bool.

3. Disporre il motto:

Come # 2; è probabile che la parte bool venga ignorata. Inoltre, forse vorresti usare e object al posto di una struct.

4. Restituito puntatore invece:

Una combinazione di quanto sopra. La maggior parte degli sviluppatori supporterà semplicemente che nullptr non verrà mai restituito.

Una quinta opzione: 5. Restituisce un valore di guardia.

Poiché il punto è in un'immagine, le sue coordinate sono sempre zero o positive. Il ritorno di un valore di guardia, ad esempio (-1,-1) , è comune ma può causare errori in seguito nel codice poiché anche il valore di guardia sarà molto probabilmente ignorato.

Non c'è una buona risposta a questo. Tutte le opzioni hanno problemi. Il meglio che puoi fare è seguire la pratica più comune di coloro che useranno la funzione.

    
risposta data 23.02.2018 - 15:31
fonte

Leggi altre domande sui tag