8

I have heard that one of the recommendations of Modern C++ is to use emplace_back instead of push_back for append in containers (emplace_back accept any version of parameters of any constructor of the type storage in the container).

According to the standard draft N3797 23.3.6.5 (1), say that:

Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.

This specify what happen when no reallocation is needed, but leave open the problem when the container need to grow.

In this piece of Code:

#include <iostream>
#include <vector>

int main() {
    std::vector<unsigned char> buff {1, 2, 3, 4};
    buff.emplace_back(buff[0]);
    buff.push_back(buff[1]);
    for (const auto& c : buff) {
        std::cout << std::hex << static_cast<long>(c) << ", ";
    }
    std::cout << std::endl;
    return 0;
}

Compiled with VC++ (Visual Studio 2013 Update 4) and GCC 4.9.1 (MinGW) in Debug in Windows 8.1.

When compiled with VC++ the output is:

1, 2, 3, 4, dd, 2

When compiled with GCC the output is:

1, 2, 3, 4, 1, 2

Checking the implementation of emplace_back in VC++ the difference is that the first lines of code, check if the container need to grow (and grow if it's needed), in the case that the container need to grow, the reference to the first element (buff[0]) received in the emplace_back method is invalidated and when the actual setting of the value in the new created element of the container happen the value is invalid.

In the case of the push_back work because the creation of the element to append is made in the parameter binding (before the possible grow of the container).

My question is: This behavior, when the container need to grow because a call to emplace_back and the parameter is a reference to the same container is implementation defined, unspecified or there is a problem in the implementation of one of the compiler (suppose in VC++ as GCC behavior is more close to the expected)?

NetVipeC
  • 4,402
  • 1
  • 17
  • 19
  • 3
    Is this http://open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#760 ? – dyp Dec 08 '14 at 19:05
  • 1
    I wonder why and how it could possibly be expected to work. – n. m. could be an AI Dec 08 '14 at 19:16
  • Related: [Is it safe to push_back an element from the same vector?](http://stackoverflow.com/q/18788780) – dyp Dec 08 '14 at 19:23
  • 2
    Here's a better view of my paper: http://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html – Howard Hinnant Dec 08 '14 at 19:24
  • 1
    Related/duplicate: [Can std::vector emplace_back copy construct from an element of the vector itself?](http://stackoverflow.com/q/24908718) – dyp Dec 08 '14 at 19:25
  • Yes, the `open-std` link describe exactly this problem. Sadly the proposed solution is adding to the standard that this kind of parameters (shall not be elements or sub-object of elements of the container, even without diagnostic). – NetVipeC Dec 08 '14 at 19:25
  • Thx for the links and wisdom. – NetVipeC Dec 08 '14 at 19:27
  • @dyp: Thanks, done: http://howardhinnant.github.io – Howard Hinnant Dec 08 '14 at 19:28
  • 1
    Reposting from [a comment by @HowardHinnant](http://stackoverflow.com/questions/24908718/can-stdvector-emplace-back-copy-construct-from-an-element-of-the-vector-itself?lq=1#comment38698932_24908718) from a related question: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2164 where he objects to the proposed resolution of LWG 760 which I've linked above. – dyp Dec 08 '14 at 19:31
  • Regarding the first paragraph of your question, Scott Meyers just recently gave a talk with a deep dive into that specific guideline, worth watching: http://youtu.be/smqT9Io_bKo?t=14m (he also refers to Howard Hinnant's benchmark, mentioned in the previous comments) – Massimiliano Dec 21 '14 at 07:30

1 Answers1

0

When you used &operator[], that returned a reference. You then used emplace_back which caused reallocation, and thus invalidated all past references. Both of these rules are well defined. The correct thing that should happen is an exception. I actually expect the VC++ version to throw an exception if you are running the debug version under the debugger.

push_back has the same two rules, which means it will also do the same. I am almost certain, swapping the two lines emplace_back/push_back will result in the same behavior.