Consider the following code, where T
is a member pointer:
#include <cassert>
struct Foo { double v = 0; };
int main()
{
using T = double Foo::*;
T p1 = nullptr;
T p2 = T();
T p3{};
T * pp4 = new T();
T * pp5 = new T{};
assert(p1 == p2);
assert(p1 == p3);
assert(p1 == *pp4); // Fails on MSVC
assert(p1 == *pp5); // Fails on MSVC
delete pp4;
delete pp5;
}
clang and gcc compile and run this without problems (live on godbolt). However, MSVC (e.g. in version 19.35, but also others) does not: It compiles, but the asserts for pp4
and pp5
fail. If I understand the cppreference for value initialization correctly, pp4
and pp5
should be zero initialized, where in turn I would expect the resulting value to compare equal to nullptr
. Printing the values to cout
shows that all values are 0, except for pp4
and pp5
which are 1. Is this a MSVC bug, or is my understanding of member pointers and reading the cppreference wrong?
Background: Of course, having dynamically allocated member pointers is rather uncommon. I stumbled upon this while testing whether my tiny::optional
can cope with member pointers (optional<double Foo::*>
). Internally, the library uses placement new to construct the value, which shows the same issue.
MSVC is not standard conformant here because T = double Foo::*
is a scalar type and new T()
is value initialization which will result in zero initialization in this case.
This can be seen from value initialization:
To value-initialize an object of type
T
means:
- (8.1)
- (8.2)
- (8.3) otherwise, the object is zero-initialized.
This means that zero initialization will be performed here.
Next, from zero initialization:
To zero-initialize an object or reference of type T means:
- if
T
is a scalar type (6.8.1), the object is initialized to the value obtained by converting the integer literal0
(zero) toT
;83
This means that the effect is the same as if we were to write T * pp4 = 0;
and T * pp5 = 0
. Thus, pp4
and pp5
both should compare equal to nullptr
.