While looking into some code, I came across a construct with the following line:
if (const auto& foo = std::get_if<MyType>(&bar)) // note the ampersand!
where bar
is a std::variant<MyType, OtherType>
. The problem here is that get_if
may return a null pointer and I don't understand why the statement works.
Consider this similar MCVE:
#include <iostream>
struct Foo { int a = 42; };
Foo* f() { return nullptr; }
int main() {
const auto& foo = f(); // Returns a nullptr that binds to Foo*& - UB?
//static_assert(std::is_same<decltype(foo), const Foo*&>::value); // -> Fails
//const Foo*& bar = f(); // -> Fails
if (foo) std::cout << foo->a << std::endl;
else std::cout << "nullpointer" << std::endl;
}
The first line of main()
works fine, and I would expect the type of bar
to be const Foo*&
, but the static assertion fails. Unsurprisingly, the following line also fails to compile with cannot bind non-const lvalue reference of type 'const Foo*&' to an rvalue of type 'const Foo*'
.
What happens in the first statement of main
? Is this UB or does the standard contain some hidden secret that allows this to be legal? What is the type of bar
?
A NULL pointer dereference occurs when the application dereferences a pointer that it expects to be valid, but is NULL, typically causing a crash or exit. NULL pointer dereference issues can occur through a number of flaws, including race conditions, and simple programming omissions.
Of course, your compiler is free to give you a null reference in that case, and virtually all compilers will do that: The reference is just a pointer under the hood, and it shares its storage with a pointer you set to nullptr .
The preferred way to write a null pointer constant is with NULL . This is a null pointer constant. You can also use 0 or (void *)0 as a null pointer constant, but using NULL is cleaner because it makes the purpose of the constant more evident.
auto will deduce type and the variable will have the same type as the parameter u (as in the // 1 case), const auto will make variable the same type as the parameter u has in the // 2 case.
Note that for const auto& foo
, const
is qualified on the auto
part, i.e. the pointer but not the pointee. Then the type of foo
would be Foo* const &
, which is a reference to const
(pointer to non-const
Foo
), but not const Foo* &
, which is a reference to non-const
(pointer to const
Foo
).
And the lvalue-reference to const
could bind to rvalue returned by f()
, so const auto& foo = f();
works fine; const Foo*& bar = f();
won't work because bar
is an lvalue-reference to non-const
; which can't bind to rvalue. Changing the type of bar
to const Foo * const &
or Foo* const &
(same as foo
) would make it work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With