Can an object with uninitialized fields be safely added in std::vector?
Asked Answered
R

1

8

In the following program, default constructor of struct A does not initialize its field v. Then in a constant expression, std::vector<A> is emplaced with A() object:

#include <vector>

struct A {
  constexpr A() noexcept {}
  int v;
};

constexpr bool f() {
    std::vector<A> as;
    as.reserve(1);
    as.emplace_back();
    return true;
}
static_assert( f() );

MSVC compiler complains about the reading of uninitialized variable:

<source>(14): error C2131: expression did not evaluate to a constant
<source>(11): note: failure was caused by a read of an uninitialized symbol
<source>(11): note: see usage of 'A::v'

But both GCC and Clang are fine with the program. Online demo: https://godbolt.org/z/addx11aTT

Which compiler is correct here?

Roderic answered 10/8, 2023 at 21:7 Comment(5)
I don't see where value of v is read in this code (as reported by the message) -- just conjecturing here, perhaps it is an interaction with MSVC debug-build code to detect uninitialized access?Pentylenetetrazol
@Pentylenetetrazol I believe it is somewhere in reallocation part of the vectorAdna
@Adna there cannot be any reallocation of vector containing objects that exist, in this exact programPentylenetetrazol
as.emplace_back(); returns an unused reference to the appended A object. And that contains an uninitialized value.Demers
Regardless of whether or not the shown program is well-formed (which I think it is), in order to "safely" use this construction one must not perform any operation on std::vector that might copy/move the indeterminate value. So no modifications except decreasing capacity, increasing capacity only from empty state, and adding/removing elements at/from the end (if capacity is free). No copying of the vector itself either.Declaim
D
1

Looks like MSVC is reporting a read error. Apparently by evaluating then discarding the returned reference form emplace_back() rather than just discarding it. MSVC does demonstrate a bug in a simplified version. See below.

The problem isn't allocation in vector, it's that the emplace_back() is a method that returns an expression, a reference to the added element.

This can be also seen by the following which creates an A object, provides a reference to it, then, when the reference is evaluated it causes an error in MSVC. It should not, as @user17732522 pointed out in comments. It should be discarded as an unused lvalue.

struct A {
    constexpr A() noexcept {}
    int v;
};

constexpr bool f() {
    A a;
    A& b = a;
    // b;   // uncommenting this causes MSVC to fail
    return true;
}
static_assert(f());

int main() {}

Compiles fine but if the b, a reference to the objects is evaluated this error ensues:

Message     failure was caused by a read of an uninitialized symbol 

This is a MSVC bug.

Demers answered 10/8, 2023 at 22:56 Comment(8)
And why would b; cause a read? This looks like a compiler bug to me.Declaim
@Declaim reading an uninitialized value is UB. And b is an expression that gets evaluated even if not used.Demers
But evaluating b doesn't cause any read. It is a lvalue expression to which no lvalue-to-rvalue conversion is applied.Declaim
@Declaim it's an expression that is the value of the object b refers to. That is a.Demers
The lvalue result of the expression is the object a, not the value of the object (which would be a prvalue). No value of a is read.Declaim
b is an id_expressionDemers
Yes and it has value category "lvalue" (timsong-cpp.github.io/cppwp/n4861/…) and it is also a discarded-value expression which specifically has the lvalue-to-rvalue conversion applied only if its type is volatile-qualified (which it isn't here). The lvalue-to-rvalue conversion followed by temporary materialization is what would read the value of a.v via the implicit copy constructor.Declaim
@Declaim Yep. You're right! I didn't realize these were discarded. The only time I have ever used something like that was with volatile in embedded apps. Thanks for following up on my boneheaded understanding.Demers

© 2022 - 2024 — McMap. All rights reserved.