The implicit conversion via constructor `OopLoadProxy(P* addr)` can cause problems when using the access API and nullptr.
For example code like
`oop o = (_obj == NULL) ? nullptr : NativeAccess<>::oop_load(_obj);`
will compile but is wrong and will crash.
This is because it will be interpreted as:
`oop o = static_cast<oop>((_obj == NULL) ? OopLoadProxy(nullptr) : NativeAccess<>::oop_load(_obj));`
while the intent of the programmer probably was:
`oop o = (_obj == NULL) ? (oop)nullptr : NativeAccess<>::oop_load(_obj);`
or more explicitly:
`oop o = (_obj == NULL) ? static_cast<oop>(nullptr) : static_cast<oop>(NativeAccess<>::oop_load(_obj));`
Marking `OopLoadProxy(P* addr)` explicit will make this example, and similar code not compile.
A side note:
OopLoadProxy objects have more gotchas (like evaporating loads), and is not the most ergonomic requiring explicit casts to work and compile in both debug and release. So there are probably more things that can be improved.
For example code like
`oop o = (_obj == NULL) ? nullptr : NativeAccess<>::oop_load(_obj);`
will compile but is wrong and will crash.
This is because it will be interpreted as:
`oop o = static_cast<oop>((_obj == NULL) ? OopLoadProxy(nullptr) : NativeAccess<>::oop_load(_obj));`
while the intent of the programmer probably was:
`oop o = (_obj == NULL) ? (oop)nullptr : NativeAccess<>::oop_load(_obj);`
or more explicitly:
`oop o = (_obj == NULL) ? static_cast<oop>(nullptr) : static_cast<oop>(NativeAccess<>::oop_load(_obj));`
Marking `OopLoadProxy(P* addr)` explicit will make this example, and similar code not compile.
A side note:
OopLoadProxy objects have more gotchas (like evaporating loads), and is not the most ergonomic requiring explicit casts to work and compile in both debug and release. So there are probably more things that can be improved.