3

I know moved from objects are in unspecified but destructible state and I know generally that means that they can own memory, file handles... But I do not know if moved from std::strings and std::vectors are allowed to own any memory.

So for example is the following function potentially leaking memory or is it fine according to C++ standard?

void f(){
    std::aligned_storage_t<sizeof(std::string), alignof(std::string)> memory;
    std::string& src = *new (&memory) std::string ("98->03->11->14->17->20");
    std::string dest(std::move(src ));
}

notes:

  • I am interested in ISO standard, I know that for most obvious implementation src should not be owning any memory after move, I am interested in "legal" status of this code.

  • I know code presented here is not "proper" way to code in C++, it is just an example to explain my question

  • I am asking specifically about std::string and std::vector, I know this is not generally true
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    If the moved-from `std::string` "owns" any memory, it will get released when the moved-from object gets destroyed. Every constructed object must be destroyed. A moved-from object is no exception. Whether the moved-from object actually owns any memory, that it is required to release upon its own destruction, is unspecified. The shown code is undefined behavior, since the `std::string` object in aligned storage never gets destroyed. To simply fix it, explicitly invoke its destructor. Now it's defined behavior (unless a thrown exception leaves this scope without destroying the object. – Sam Varshavchik Sep 12 '17 at 22:31
  • 1
    All we know is it is in a valid, but, unspecified state. – NathanOliver Sep 12 '17 at 22:33
  • 1
    Hard as I try, I cannot imagine why one would have reason *not* to invoke the destructor directly, as per the usual norm of placement construction/destruction, *regardless* of whether a move has been done or not. And I see no conceivable standard-supported exceptions on that regarding `std::vector` or `std::basic_string`. – WhozCraig Sep 12 '17 at 22:36
  • 2
    This is similar to asking whether it would be a leak to construct an empty `vector` or `string` and not destroy it. "Owns memory" does not correspond to any term used or described in the Standard. (There are owning and non-owning smart pointers and `std::function` objects, but that sense of "non-owning" doesn't mean what you're asking about here, either.) – aschepler Sep 12 '17 at 22:45
  • One thing that comes to mind - STL libraries that implement "Small String Optimization" (SSO) for `std::string`. When an SSO-enabled string contains small content, the character data is not stored in dynamic memory and thus *cannot* be moved to another string object. The content can only be copied from one buffer to another. The moved-from string retains ownership of its memory buffer, though its `size` is reset to 0 during the "move". For non-SSO strings, and strings with content longer than the SSO buffer, memory is allocated dynamically and thus can transfer ownership between string objects – Remy Lebeau Sep 13 '17 at 00:56
  • @RemyLebeau yes, but SSO strings that fit in the buffer do not use any heap memory. – NoSenseEtAl Sep 13 '17 at 02:11
  • @NoSenseEtAl: I know, but they do nonetheless own the memory for their SSO buffers, and that is the point I was making. "*is a moved std::string allowed to own ANY memory?*" - YES, if it makes sense for it to do so. The memory simply wouldn't have any valid data in it after a move, though. – Remy Lebeau Sep 13 '17 at 02:49

2 Answers2

3

No; pathological implementations are free to move-construct any specific std string as a copy, leaving the source alone, so long as the operation doesn't throw. (there must be a length beyond which this does not happen to obey O(1) guarantee).

A std vector's iterator invalidation rules are tighter; the move would have to be pathologically evil to own memory afterwards. Similarly it may not throw, even if allocation fails.

Both if these are unreasonable possibilities; but so is skipping destruction.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    The constant-time complexity guarantee prohibits unconditionally copying all strings for move-construction. – user2357112 Sep 12 '17 at 22:54
  • 1
    @user23 sure, but it could copy all strings up to length 527432. Any heap based copies is already pathological; so why stop at mere insanity? But good point; edited in. – Yakk - Adam Nevraumont Sep 12 '17 at 22:58
2

There's nothing in the standard that requires a moved-from object to no longer own any resources. (Other than performance guarantees but I don't see them preventing such ownership in this case).

Regarding your program, see [basic.life/4]:

For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

The part of this "any program that depends on the side effects" is not as precise wording as we like to see in a standards document, but it's usually interpreted to mean "anything other than a destructor that has no observable behaviour" . We don't know what the library implementation might have put in its destructor for vector and string (e.g. it could have debugging tracking in debug mode).

So I would say your program causes undefined behaviour by omitting the destructor call , although there is some room to debate.

M.M
  • 138,810
  • 21
  • 208
  • 365