3

In C# there is a null conditional operator ?.:

MyObject o = null;

o?.doSomeThing();

Which means:

if(o != null)
    o.doSomeThing();

I would like to have something similar in C++:

MyObject* o = nullptr;

o?>doSomeThing(); // or o?->

It is very useful if one write this:

o?.getO()?.getO()?.doSomeThing();

So I have two questions:

  1. Today we use macros for one line conditional execution. Is there a short and handy way in C++14 not to use macros?
  2. Are there discussions or proposals anywhere? I haven't found anything.
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Tobias Wollgam
  • 761
  • 2
  • 8
  • 25
  • 1
    No, @oznog std::optional does nothing of that sort, or anything remotely similar. – Sam Varshavchik Nov 01 '19 at 17:11
  • 1
    @PaulR that doesn't work if doSomeThing returns something that cannot be converted to bool (I think). But else it is a nice solution, cause chaining works. – Tobias Wollgam Nov 01 '19 at 17:12
  • @JesperJuhl `if(o && o->getO() && o->getO()->getO()) o->getO()->getO()->doSomeThing();` Ok, it is syntactic suggar ... – Tobias Wollgam Nov 01 '19 at 17:20
  • @TobiasWollgam and it is syntactic sugar that C++ does not provide. Simple as that. – Jesper Juhl Nov 01 '19 at 17:31
  • 1
    My answer to Jesper is false, must be: `if(o) if(auto o1 = o->getO()) if(auto o2 = o1->getO()) o2->doSomeThing();` Else the getters are executed twice and the second execution could return nullptr or another object. I would not write this as one liner. – Tobias Wollgam Nov 01 '19 at 17:58
  • 1
    Similar to this question: https://stackoverflow.com/questions/45149760/is-there-a-safe-navigation-operator-for-c – Eljay Nov 01 '19 at 18:37
  • a very close simulation: https://stackoverflow.com/a/73790172/1097597 – crazybie Sep 20 '22 at 16:57

2 Answers2

1

No, there is no syntax to do this, and no way to create said syntax.

Likewise there are no proposals (that I'm aware of) to add it.

Instead, write out the null checks yourself, or better yet make it so that you don't need them, by restructuring your code to reduce the number of places that things can be "null".

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

WRT question #1:

constexpr and inline functions are the type-safe C++ equivalents of macro functions. They're not guaranteed to inline your code, but they give the compiler permission to make copies as needed, and an aggressive compiler will do the inlining for you. A lot of standard library classes make that assumption (e.g. std::vector<T>::size()).

EDIT: Ok, you guys are viscious, but fair. Here's a way to use it to solve this problem:

EDIT: Updated to compile in C++14 as asked. I know this has been marked duplicate, and no one is going to look at it, but the other links are C++17 only. It also terminates on non-pointer members and returns a pointer to them, which is more in line with C#, which converts value types to Nullable.

#include <cstdio>
#include <type_traits>

template <typename Class>
struct SafePtrImpl { 
    Class* ptr;

    SafePtrImpl(Class* ptr) : ptr(ptr) { }

    template <typename TClass, typename TResult, std::enable_if_t<std::is_pointer<TResult>::value, std::nullptr_t> = nullptr>
    inline SafePtrImpl<
        std::remove_pointer_t<
            std::remove_reference_t<
                TResult>>> operator ->*(TResult TClass::*member) {
        return SafePtr(ptr ? ptr->*member : nullptr);
    }

    template <typename TClass, typename TResult, std::enable_if_t<!std::is_pointer<TResult>::value, std::nullptr_t> = nullptr>
    inline TResult* operator ->*(TResult TClass::*member) {
        return ptr ? &(ptr->*member) : nullptr;
    }
};

template <typename TClass>
SafePtrImpl<TClass> SafePtr(TClass* ptr) { return SafePtrImpl<TClass>(ptr); }

// --

struct Foo {
    int x;
};

struct Bar {
    Foo* y;
};

int main() {
    Foo a { 42 };
    Bar b { &a };
    Bar c { nullptr };
    Bar* d = nullptr;

    int* b_y_x = SafePtr(&b)->*&Bar::y->*&Foo::x;
    int* c_y_x = SafePtr(&c)->*&Bar::y->*&Foo::x;
    int* d_y_x = SafePtr(d)->*&Bar::y->*&Foo::x;
    printf("b: %08x %d\n", b_y_x, *b_y_x);
    printf("c: %08x\n", c_y_x);
    printf("d: %08x\n", d_y_x);

    return 0;
}

https://godbolt.org/z/RYZ1EV

Output:

b: 3aa42aec 42
c: 00000000
d: 00000000
parktomatomi
  • 3,851
  • 1
  • 14
  • 18