2009-11-23 5 views

risposta

12

Non c'è un modo per fare questo per impostazione predefinita in C++, ma si potrebbe scrivere uno:

in C# il ?? operatore è definito come

a ?? b === (a != null ? a : b) 

Così, il metodo C++ sarà simile

Coalesce(a, b) // put your own types in, or make a template 
{ 
    return a != null ? a : b; 
} 
+12

In questo metodo, b viene valutato anche se a non è nullo. Questo potrebbe essere un problema se b ha effetti collaterali. – Amnon

+3

@Amnon: Infatti. Considera: 'p = Coalesce (p, new int (10));'. Inoltre, sembra che in C# l'operando di destra non possa essere NULL (non può essere controllato in fase di compilazione, dato che non ci sono tipi nullable in C++?). D'altra parte: c'è così tanto bisogno in C++, quindi non puoi semplicemente digitare 'a? a: b; '? – UncleBens

+1

E se è C++ 11 o superiore, usa 'nullptr'. Non c'è nullo in C/C++, solo 'NULL' è definito in alcune intestazioni. http://stackoverflow.com/questions/1282295/what-exactly-is-nullptr –

2

ne dici di questo?

#define IFNULL(a,b) ((a) == null ? (b) : (a)) 
+2

attenti a IFNULL (a ++, b) Sì, mi rendo conto che vorresti comunque guardarti da ciò, se pensi che possa essere nullo. IFNULL (SomeClass.DoSomethingReallyLong(), "") causa anche problemi. – McKay

+0

Bene, mi serve per essere pigro. L'utilizzo di un metodo di modello è sicuramente la strada da percorrere qui, poiché evita effetti collaterali e avrà prestazioni equivalenti (o migliori). Aggiungerò un +1 sulla soluzione di @ McKay ora. :-) –

+3

Un modello di funzione non ha necessariamente prestazioni uguali o migliori. Questo "cortocircuito" ('b' non viene valutato a meno che' a' sia null), mentre gli argomenti di una funzione vengono sempre valutati. –

1

Utilizzo di modelli e C++ 11 lambda. Il primo argomento (lato sinistro) viene valutato solo una volta. Il secondo argomento (lato destro) viene valutato solo se il primo è falso (notare che 'if' e '?' Convertono staticamente l'espressione fornita in bool, e quei puntatori hanno 'esplicito bool operatore() const' che è equalivent a '! = nullptr')

template<typename TValue, typename TSpareEvaluator> 
TValue 
coalesce(TValue mainValue, TSpareEvaluator evaluateSpare) { 

    return mainValue ? mainValue : evaluateSpare(); 
} 

Esempio di utilizzo

void * const  nonZeroPtr = reinterpret_cast<void *>(0xF); 
void * const otherNonZeroPtr = reinterpret_cast<void *>(0xA); 

std::cout << coalesce(nonZeroPtr, [&]() { std::cout << "Never called"; return otherNonZeroPtr; }) << "\n"; 

sarà solo stampare '0xf' nella console. Dovendo scrivere un lambda per i RHS è un po 'di testo standard

[&]() { return <rhs>; } 

ma è il meglio che si può fare se uno manca il supporto per la sintassi del linguaggio.

+0

Questa è l'unica risposta che offre la semantica identica di un operatore a coalescenza nulla. Probabilmente raramente vale la pena, però. In realtà, questa funzione deve solo essere nella lingua. –

1

Voglio solo di ampliare la risposta di @Samuel Garcia generalizzando il modello e l'aggiunta di macro di supporto per ridurre il lambda boilerplate:

#include <utility> 

namespace coalesce_impl 
{ 
    template<typename LHS, typename RHS> 
    auto coalesce(LHS lhs, RHS rhs) -> 
     typename std::remove_reference<decltype(lhs())>::type&& 
    { 
     auto&& initialValue = lhs(); 
     if (initialValue) 
      return std::move(initialValue); 
     else 
      return std::move(rhs()); 
    } 

    template<typename LHS, typename RHS, typename ...RHSs> 
    auto coalesce(LHS lhs, RHS rhs, RHSs ...rhss) -> 
     typename std::remove_reference<decltype(lhs())>::type&& 
    { 
     auto&& initialValue = lhs(); 
     if (initialValue) 
      return std::move(initialValue); 
     else 
      return std::move(coalesce(rhs, rhss...)); 
    } 
} 

#define COALESCE(x) (::coalesce_impl::coalesce([&](){ return (x); })) 
#define OR_ELSE ); }, [&](){ return (

Usando le macro, si può solo:

int* f(); 
int* g(); 
int* h(); 

int* x = COALESCE(f() OR_ELSE g() OR_ELSE h()); 

I spero che questo ti aiuti.