(Edit: Limited c++ experience, thanks for explaining)
(Edit: Limited c++ experience, thanks for explaining)
In C++ you can have pointers to objects, but they're explicitly denoted by an asterisk. The C++ equivalent would be "Object* myobject;"
However, you can also directly have object variables in C++. That's not something that exists in Java. A variable declared like, "Object myobject;" can never be null. The myobject variable is a value, just like i in "int i = 5;" is a value.
In C++ a useful heuristic could be to think of values like Java primitives.
A C++ integer takes up some amount of space. Maybe it's four bytes on your platform. Integers on your platform take up four bytes. Not "four bytes plus a pointer", but four bytes and that's it.
In those four bytes you can store 2^32 values. There's nowhere else to store whether the integer is null -- if you wanted to signal to someone that the value was "missing", you would either have to,
- Come up with your own "in-band" signal, like "-(2^31) means 'missing'", or
- Have an out-of-band signal, like a separate boolean variable, or keep a pointer to "integer or null". (Pointers being nullable is arguably kinda weird still.)
Functions that return an integer in C++ on your platform will put four bytes "in the right place" on the stack. (Or in a register? I don't really know what calling conventions look like...)
Again, there's nowhere to signal that the value is missing -- if you don't fill in that memory location or that register, well, there will just be some other (undefined) value sitting in its place that the caller will read. Ditto functions that expect integer arguments etc.
Now, this has some interesting consequences: for stack-allocated values in C++, "object identity" tends to be preserved less often than in Java. When you put an object into a `vector`, you'll probably put a copy there. When you mutate that copy or call some function on it, you're not mutating the original. You can get back to "Java semantics" of object identity and nullability etc by using pointers and explicit heap storage.
On a register. I don't know of any mainstream platforms which return simple values like an integer through the stack.
For larger values (larger than a word or a couple of words), on the other hand, usually the calling function passes the called function a pointer to a location on the stack where it should write the result.
Either way, like you said "there's nowhere to signal that the value is missing".
Object& obj = other_obj;
For this reason, when passing objects by reference, references should be preferred to pointers where possible. Consequently, I would translate Java code like: void f(Object obj) { ... }
Object obj = ...
f(obj);
Into C++ code like: void f(Object& obj) { ... }
Object& obj = ...
f(obj);
(Setting aside various other concerns like move semantics and const-correctness.)So the value of an optional is either a valid value or it has no value. The default constructed std::optional<T> has no value
Also, in c++ if you try to use an empty value in an optional<T> it isn't using undefined behavior as dereferencing a null pointer would, plus with optional you get value semantics so things like copy are deep vs copying the pointer. Not sure about C# nullables, but I guess if you copy the .value member it is deep.
You can't do for instance
struct foo {
// whatever
};
foo my_function() {
return nullptr;
}
and thus, std::optional<int> my_function() {
return nullptr;
}
does not compile (rightly). struct Vector
{
int x, y;
}
public class Program
{
public static void Main()
{
Vector v = null;
}
}
Also does not compile:"Cannot convert null to 'Vector' because it is a non-nullable value type"
Basically, just given `public foo myFunction() { ... }` there's no easy way to know
Structs are value types and cannot be heap allocated like in C++.
Doing new foo() will only call the constructor while stack allocating, while in C++ will put it on the heap.
Also, the only way structs ends on the heap is by implicit boxing when converting to an interface.
Finally, yes structs can be nullable, only when they are declared as such, Typename?.
Does mouse hovering work when you're reviewing a diff on gitlab?
Unless you can magically understand it is a struct just by looking at the name, instead of a typedef, using definition or even a macro.
If for example you have a function with a return type of string, it must return a string. It cannot return nullptr, NULL, or 0. It could return an empty string. But in some cases you might want to distinguish between empty string and no result, in that case you would want to the return type to be std::optional<std::string> .
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p032...
Returns a value (as in std::optional) than can be null. If so you will get an error explaining what happened.
https://docs.oracle.com/javase/8/docs/api/java/util/Optional...
The Kotlin language (JVM-based) got this right: https://kotlinlang.org/docs/reference/null-safety.html
In the meantime, Java coding best practices dictate treating all references as non-null in first-party code (defensively checking references received from third-party code, such as with Objects.requireNonNull), and avoiding nullable references in favor of Optional.
The issue of Optional<T> being nullable is an extremely uncommon one - I have seen it happen once ever, and there were way bigger issues going on that happened to manifest that way. It's one of those "that's dumb but ultimately makes no difference" problems that many languages have.