Home > Mobile >  Will elements inserted by emplace_back always pick up the ::end() iterator?
Will elements inserted by emplace_back always pick up the ::end() iterator?

Time:01-05

If I'm inserting an element at the back of a container (e.g. with emplace_back(), is the new element always guaranteed to be at the previous ::end() position?

Compare the following example: Here this works because I can pass the iterator to the newly inserted entity object which relies on it in turn to output "Hello World" to the console. I want to know if this is guaranteed to always work for every container.

EDIT: As pointed out in the comments, std::vector and consorts are already out because of reallocation after insert. But I'm still wondering if it is true for all containers that are iterator-preserving after insert.

void { fn_(); } std::function fn_; }; using list_t = std::list; using iterator_t = list_t::iterator; int main() { list_t mylist; mylist.emplace_back([it = mylist.end()]{ it->print(); }); for (auto& item : mylist) { item(); } }'),l:'5',n:'0',o:'C++ source #1',t:'0')),k:57.59791122715404,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:g122,deviceViewOpen:'1',filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!(),options:'-Wall -Os --std=c++20',selection:(endColumn:24,endLineNumber:8,positionColumn:24,positionLineNumber:8,selectionStartColumn:24,selectionStartLineNumber:8,startColumn:24,startLineNumber:8),source:1),l:'5',n:'0',o:' x86-64 gcc 12.2 (Editor #1)',t:'0')),header:(),l:'4',m:41.3677130044843,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compilerName:'x86-64 gcc 12.2',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output of x86-64 gcc 12.2 (Compiler #1)',t:'0')),k:50,l:'4',m:58.632286995515706,n:'0',o:'',s:0,t:'0')),k:42.40208877284596,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow noreferrer">Demo

#include <cstdio>
#include <functional>
#include <list>

struct entity
{
    entity(std::function<void()> fn)
        :   fn_( fn )
    { }

    auto print()
    {
        printf("Hello World!");
    }

    auto operator()() -> void {
        fn_();
    }

    std::function<void()> fn_;
};

using list_t = std::list<entity>;
using iterator_t = list_t::iterator;

int main()
{
    list_t mylist;

    mylist.emplace_back([it = mylist.end()]{ it->print(); });

    for (auto& item : mylist) {
        item();
    }
}

CodePudding user response:

In your shown code the lambda will capture the end iterator at the time the lambda is instantiated. Since the end iterator does not point towards a valid node you are not allowed to dereference it. it is important to understand, that your lambda has already captured the end iterator by value and this iterator will never update by itself. this holds for every container in the std library, i guess it will even hold for every custom container in the wild.

you dereference the end iterator when calling the lambda and by doing this you are in undefined behaviour land. your program is illformed and no output is guaranteed.

CodePudding user response:

"... when it possibly is not a placeholder anymore" (from the comments to original question) is not correct. Take the example:

int test = 10;
void func(int a = test)
{
   std::cout << test << '\n';
}

int main()
{
   func();
   test = 100;
   func();
}

Here, the output is

10
100

Basically my point is that default arguments are assigned when the function is called. Thus, in your example, it gets assigned to the end iterator of the list, and so it->print() is undefined behavior as you're dereferencing the end iterator with it->print() (you can think of it->print() as just (*it).print() but that's probably obvious).

  • Related