As an optimization, string fields are initialized with a pointer to a global
immutable std::string instance and create a local std::string only when
"set". If a field has hasbits, it presents a possibility that the hasbit is set
but the string field is still pointing to the global empty string instance.
This can happen, for example, when the field is implicit-presence but hasbit
has been generated for it.
Maintaining an invariant that hasbit is set iff string is nondefault can
simplify the implementation of destructors and message.Clear(). The code would
not need to branch further after scanning hasbits, instead it can always assume
that a local std::string object exists as soon as it sees that the hasbit is
set.
However, this does require an else block in the merge implementation of
implicit-presence string fields. When hasbits are implemented for
implicit-presence string fields, merging from a non-present (i.e. empty) string
field requires a nondefault std::string instance to be created. On the other
hand, branches in Clear() can be eliminated. We think this is the right
tradeoff because:
1. The allocation of nondefault string instance can only happen when the source
proto has hasbit set but the field is empty. This is a relatively rare
scenario.
2. Clear() is called every time a protobuf object is "overwritten" via an
assignment operator or ParseFrom(). This happens probably more frequently than 1.
PiperOrigin-RevId: 691951661