32

Is there a C++ equivalent for C# null coalescing operator? I am doing too many null checks in my code. So was looking for a way to reduce the amount of null code.

Ajay
  • 18,086
  • 12
  • 59
  • 105
aajkaltak
  • 1,437
  • 4
  • 20
  • 28
  • 12
    In GCC, it's `a ?: b` but that's non-portable. – MSalters Nov 26 '09 at 15:36
  • @MSalters: That's not quite it, since if `false ?: 123` is `123`, while `coalesce(false, 123)` is `false`. – einpoklum Apr 18 '23 at 08:54
  • @einpoklum: you appear to have a particular definition of `coalesce`. E.g. SQL `COALESCE(NULL, 123)` is `123`. My informal definition is "the first truthy value in a list" – MSalters Apr 18 '23 at 09:19
  • @MSalters it's not me, it's the OP... this question is about C#'s coalesce. I just posted an answer, please see the details there. – einpoklum Apr 18 '23 at 10:02

9 Answers9

17

I just found this: The ?? operator aka the Null Coalescing Operator

You also have it in C/C++ as a GNU extension using the ?: operator :

string pageTitle = getTitle() ?: "Default Title";
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
adamjhilton
  • 362
  • 3
  • 9
  • 2
    New link: https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Conditionals.html#Conditionals In short, the GNU extension lets you omit the middle of a ternary and that results in the condition becoming the return value in the case where that condition is "non-zero" – Cory-G Feb 21 '20 at 01:56
  • This is incorrect, because `false ?: 123` is `123`, while `coalesce(false, 123)` would be `false`. – einpoklum Apr 18 '23 at 08:56
13

There isn't a way to do this by default in C++, but you could write one:

in C# the ?? operator is defined as

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

So, the C++ method would look like

Coalesce(a, b) // put your own types in, or make a template
{
    return a != null ? a : b;
}
McKay
  • 12,334
  • 7
  • 53
  • 76
  • 25
    In this method b is evaluated even if a isn't null. This might be a problem if b has side effects. – Amnon Nov 23 '09 at 19:52
  • 4
    @Amnon: Indeed. Consider: `p = Coalesce(p, new int(10));`. Also, it seems in C# the right-hand operand cannot be NULL (can't be checked at compile-time, since there are no nullable types in C++?). On the other hand: is there that much need for it in C++, so that you can't just type `a ? a : b;`? – UncleBens Nov 23 '09 at 21:34
  • 5
    And if it's C++11 or above, use `nullptr`. There's no `null` in C/C++ anyway, only `NULL` is defined in some headers. http://stackoverflow.com/questions/1282295/what-exactly-is-nullptr – Csaba Toth Sep 12 '13 at 18:52
  • @UncleBens: Can you explain why in C# the right-hand operand of cannot be NULL? AFAIK, you can even chain these together ie `A??B??C` in which case it will choose the first non-NULL operand. B is the right-hand operand of `A??B` and it can be NULL, or is it because it is also the left-hand operand of `B??C`? – Lunyx Dec 11 '13 at 03:25
  • 1
    @Lunyx: In the case of `A??B??C`, `B??C` is the right hand operand of the first `??`. In other words, it's equivalent to `A??(B??C)`. Also, there is no restriction that the right hand operator cannot be null, it just results in the whole thing being null. – Cemafor Apr 09 '15 at 00:33
  • Any opportunity to make an custom operator out of this? – Vinz Nov 27 '15 at 13:45
  • @vinzenz that should be a new question. – McKay Nov 28 '15 at 16:00
  • @M.kazemAkhgary Could you elaborate? I don't see how it isn't threadsafe, but perhaps I'm missing something. To me, this code in C++ is a function that takes two pointers (not by reference), so `a` is either null, or it's a pointer to a location in memory. That can't get modified by the caller, because it's a pointer. Admittedly the contents of the memory at that location might change, but the contents of the memory at that location are not accessed (or even dereferenced) by this function. Am I missing something? – McKay Oct 21 '19 at 04:51
  • 1
    Oops, my bad, you are right. Since "a" and "b" are local variables this code is thread safe – M.kazem Akhgary Oct 21 '19 at 16:00
  • @einpoklum Could you elaborate? Perhaps you're missing the comment at the end of the line? Or the lack of a clear null that's already been discussed? Those should be simple enough for you to figure out on your own. If it's something else, let's talk about it. – McKay Apr 18 '23 at 17:26
  • @McKay: The snippet at the bottom is something could be converted into C++, but isn't C++ yet. And the conversion is not trivial. – einpoklum Apr 18 '23 at 17:46
  • @einpoklum it depends on your definition of "trivial". I think especially the non templated version is trivial. The answer was meant to be instructive, not to solve all of the worlds problems. I even have a comment stating a clear limitation of how it isn't c++ yet. I'm sorry it doesn't meet your standards. – McKay Apr 20 '23 at 05:06
5

Using templates and C++11 lambdas:

template<typename TValue, typename TRhsEvaluator>
TValue coalesce(TValue lhsValue, TRhsEvaluator evaluateRhs) {
     return lhsValue ? lhsValue : evaluateRhs();
}
  • The first argument (left-hand side) is only evaluated once.
  • The second argument (right-hand side) is only evaluated if the first argument is false.

Note that if and ? statically cast the provided expression to bool, and that pointers have a built-in explicit operator bool() const operator which is equalivent to != nullptr

Example usage:

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

std::cout << coalesce(nonZeroPtr, [&] () {
    std::cout << "Side-effect. Should never be printed" << std::endl;
    return otherNonZeroPtr;
}) << std::endl;

The code above will only print 0xf to the console.

The right-hand side needs to be wrapped inside a lambda - we cannot avoid that boilerplate. Really the language should provide a null coalescing operator out of the box.

  • 4
    This is the only answer that offers identical semantics of a null coalescing operator. It's probably rarely worth the fuss, though. Really, this feature just needs to be in the language. – Edward Brey Oct 15 '16 at 12:28
3

Just want to expand @Samuel Garcia's answer by generalising the template and adding helper macros to cut down on 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 (

Using the macros, you can just:

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

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

I hope this helps.

Hongbin HU
  • 49
  • 2
3

Here are two macros to replicate the ?? and ?. operators. These macros ensure:

  • Arguments are evaluated only once.
  • The second param is only evaluated if the first param is null.
  • Macro variables are internally scoped using compile-time-removed lambda syntax. This avoids macro variable collisions.

Example usage:

    COA( nullPtr, goodPtr )->sayHello();
    COA( nullPtr, COA( nullPtr, goodPtr ) )->sayHello();

    COACALL( goodPtr, sayHello() );
    COACALL( nullPtr, sayHello() );

    COACALL( COA( nullPtr, goodPtr ), sayHello() );

Definitions:

    #define COA(a, b) ([&](){ auto val = (a); return ((val) == NULL ? (b) : (val)); }())
    #define COACALL(a, b) ([&](){ auto val = (a); if (val) (val->b); }());

Note: COACALL does not return results. Only use with void calls or alter to fit your needs.

Cory-G
  • 1,025
  • 14
  • 26
  • This COALESCE implementation is simple and avoids other answers drawbacks. Should be rated higher – zzz Nov 09 '21 at 23:35
1

How about this?

#define IFNULL(a,b) ((a) == null ? (b) : (a))
Justin Grant
  • 44,807
  • 15
  • 124
  • 208
  • 3
    beware of IFNULL(a++, b) Yeah, I realize you'd want to beware of that anyway if you think a might be null. IFNULL(SomeClass.DoSomethingReallyLong(), "") causes problems too. – McKay Nov 23 '09 at 19:40
  • 1
    Good point-- serves me right for being lazy. Using a template method is definintely the way to go here, since it avoids side effects and will have equivalent performance (or better). I'm going to add a +1 on @McKay's solution now. :-) – Justin Grant Nov 23 '09 at 20:27
  • 4
    A function template doesn't necessarily have equal or better performance. This "short-circuits" (`b` is not evaluated unless `a` is null), whereas a function's arguments are always evaluated. – Steve Jessop Nov 23 '09 at 22:02
  • @Steve Correct Justin's code would "perform" better in the case of IFNULL("", SomeClass.DoSomethingReallyLong()); – McKay Nov 23 '09 at 22:25
  • 2
    Also a good point. But, I think the most common use case (at least in C# when I've used the ?? operator) is where a is more expensive (e.g. database call, XML parsing) than b. Often b is a constant, like 0 or "". So I suspect the template would win out most of the time. Plus, unless you know how the macro is implemented, the double-execution wouldn't be obvious to the macro's caller, while function-call semantics are well-known so anyone with an expensive b would know to skip the function and just manually create a temporary variable. Funny, I'm arguing against my own answer... :-) – Justin Grant Nov 23 '09 at 22:37
  • And I was arguing against my own. Both have their advantages and drawbacks. – McKay Oct 26 '10 at 22:52
  • This should be the right answer. Its the only one that doesn't always evaluate (b), and doesn't require some verbose syntax usage side. Macros aren't usually the way to go, but some people are way too dogmatic about it. – c z Jun 14 '16 at 21:49
  • 2
    You could wrap in a lambda, then `a` won't ever be evaluated twice. `#define IFNULL(a, b) ([&](){ auto val = (a); return ((val) == NULL ? (b) : (val)); }()) ` – Cory-G Mar 16 '18 at 06:21
1

There is a GNU GCC extension that allows using ?: operator with middle operand missing, see Conditionals with Omitted Operands.

The middle operand in a conditional expression may be omitted. Then if the first operand is nonzero, its value is the value of the conditional expression.

Therefore, the expression

x ? : y

has the value of x if that is nonzero; otherwise, the value of y.

This example is perfectly equivalent to

x ? x : y

In this simple case, the ability to omit the middle operand is not especially useful. When it becomes useful is when the first operand does, or may (if it is a macro argument), contain a side effect. Then repeating the operand in the middle would perform the side effect twice. Omitting the middle operand uses the value already computed without the undesirable effects of recomputing it.

This extension is also supported by clang. However, you should check with the compiler you're using and portability requirements for your code before using the extension. Notably, MSVC C++ compilers do not support omitted operands in ?:.

See also related StackOverflow discussion here.

mix
  • 19
  • 2
0

Just to add to the answers mentioning the ?: operator (the "Elvis operator"): I sometimes use a helper function together with this operator that gets the underlying value of a pointer or std::optional or similar types that "wrap" a value and have a boolean conversion to indicate the presence of a value. For example:

template <typename T>
constexpr T coalesce (std::optional<T> opt) {
    return *opt;
}
template <typename T>
constexpr T coalesce (T fallback) {
    return fallback;
}

std::optional<int> opt1{5};
std::optional<int> opt2;
int val1 = coalesce(opt1 ?: 0);
int val2 = coalesce(opt2 ?: 0);

The only drawback is that this must be used carefully and won't give you a static check of correct use. E.g. you could just do coalesce(opt2) without the ?: fallback and that would be the same as doing *opt2 without first checking whether it contains anything. So the name coalesce is sort of misleading, but when used correctly it looks self-explanatory (and pretty neat) in my opinion.

egst
  • 1,605
  • 3
  • 18
  • 25
0

Reminder: C# coalesce semantics

The semantics of ??, the coalescing operator of C#, are:

p ?? q

Here, p is the left and q is the right operand of [the] ?? operator. The value of p can be nullable type, but the value of q must be non-nullable type. If the value of p is null, then it returns the value of q. Otherwise, it will return the value of p.

A C++ implementation

We first note that C++ cannot use ?? as an identifier; nor is the preprocessor willing to let us define a macro with this name. So, lets use coalesce as the identifier instead.

We need to define coalesce somehow as an infix operator with the requested semantics. This is doable with a combination of operator overloading and a macro for tying things together, so that we can write:

int* p = // whatever
int* q = // whatever
int* result = p coalesce q;

Here's an implementation:

#include <functional>

namespace detail {

struct coalesce_op {};

template <typename T>
struct coalesce_op_primed { 
    T&& lhs; 

    constexpr T&& operator+(T&& rhs) {
        return (lhs == nullptr) ? rhs : lhs;
    }
};

template <typename T>
constexpr coalesce_op_primed<T> operator+(T&& t, coalesce_op)
{
    return coalesce_op_primed<T>{std::forward<T>(t)};
}

} // namespace detail

#define coalesce + detail::coalesce_op{} +

See it in action on GodBolt.

Notes:

  • There isn't any short-circuit logic, i.e. q is evaluated even if it isn't used. One could write a macro with short-circuit logic, but it wouldn't be an infix operator.
  • We cannot use the GNU C extensions' ?: Elvis-operator, as its semantics are different. It will consider a non-null false value as reason to choose the RHS operand, i.e. false ?: 123 will yield 123, while false ?? 123 is false using the C# coalesce semantics.
  • I did not try to make this work for std::optional's, detecting nullopt. If that is required, it should also be possible with some if-constexpr or tagged-dispatch TMP.
einpoklum
  • 118,144
  • 57
  • 340
  • 684