-1

I have a list of sets:

std::list<std::set<int>> nn = {{1,2},{4,5,6}};

and I want to print out the element which end() refers to:

for (auto el : nn){
        std::cout << *el.end() << std::endl;
    }

What I get as a result is: 2 and 3.

I do not know where do these values come from. Can someone help me plz?

  • 6
    Those values come from **undefined behavior**. – Eljay Jan 03 '22 at 15:07
  • 2
    this is undefined behavior - invalid code. – Marek R Jan 03 '22 at 15:07
  • 11
    `end()` does not refer to any element, it is illegal to dereference it. If you want the last element use `*rbegin()` – Slava Jan 03 '22 at 15:07
  • 2
    `end()` is not an element of the container. It's the first position past the end of the list. – al3c Jan 03 '22 at 15:08
  • 1
    https://en.cppreference.com/w/cpp/container/set/end "acts as a placeholder; attempting to access it results in undefined behavior" –  Jan 03 '22 at 15:09
  • If you run your code in Visual C++ debug mode, I bet you will be greeted with a mean "Assertion" message box, telling you what you did was illegal. Thus you wouldn't even get to see any output due to your program stopping dead. – PaulMcKenzie Jan 03 '22 at 15:10
  • 1
    _"I want to print out the element which end() refers to:"_ Your assumption is wrong that `end()` refers to an element. `end()` signifies that there are no more elements. That you have reached the end. – Drew Dormann Jan 03 '22 at 15:13
  • 1
    You might want to read this on for loops : https://www.learncpp.com/cpp-tutorial/for-each-loops/. Normally end() refers to the position one beyond the end of a collection. – Pepijn Kramer Jan 03 '22 at 15:14
  • Has anyone already mentioned that `end()` doesn't refer to an element and accessing it is undefined behavior? – bereal Jan 03 '22 at 15:21
  • 1
    @anastaciu You're right, I thought it was a `std::list`. – François Andrieux Jan 03 '22 at 15:27
  • That is interesting: address sanitizer [fails to detect this problem :0](https://godbolt.org/z/cx3xc1ocn). – Marek R Jan 03 '22 at 15:42
  • 1
    @MarekR Depending on the implementation of `std::set`, there is likely no invalid memory access. The address sanitizer doesn't know or check all standard library call preconditions. You can add `-D_GLIBCXX_DEBUG` to have libstdc++ use a debug mode with extra checks: https://godbolt.org/z/Mav4oWzK3 – user17732522 Jan 03 '22 at 15:57

2 Answers2

2

It is not allowed to de-reference the end() iterator. Doing so causes undefined behavior. It doesn't refer to any element of the container, but one past the last element.

The reason that end() "points" after the last element, is that it is necessary to distinguish empty containers. If end() was referring to the last element and begin() to the first, then if begin() == end() that would mean that there is one element in the container and we can't distinguish the case of an empty container.

For containers that support it, to access the last element of the container you can use .back(), which will return a reference, not an iterator. But this is only allowed if there is a last element, i.e. if the container is not empty. Otherwise you have again undefined behavior. So check .empty() first if necessary.

std::set does not have the back() member and is not really intended to be used this way, but if you really want to access the last element, which is not the last element in the constructor initializer list, but the last element in the < order of the elements, then you can use std::prev(el.end()) or el.rbegin() ("reverse begin") which will give you an iterator to the last element. Again, dereferencing this iterator is only allowed if the container is not empty. (For std::prev forming the iterator itself isn't even allowed if the container is empty.)


Undefined behavior means that you will have no guarantees on the program behavior. It could output something in one run and something else in another. It could also output nothing, etc.

There is no requirement that you will get the output you see. For example, current x86_64 Clang with libc++ as standard library implementation, compiles (with or without optimization) a program that prints 0 twice. https://godbolt.org/z/nWMss1fqe

Practically speaking, assuming the compiler didn't take advantage of the undefined behavior for an optimization that drastically changes the program from the "intended" program flow, you will likely, depending on the implementation of the standard library, read some internal memory of the std::set implementation in the standard library, get a segmentation fault if the indirection points to inaccessible memory or incidentally (with no guarantees) refer to other values in the container.

user17732522
  • 53,019
  • 2
  • 56
  • 105
2

Question 1

What does end() refere to

Answer

end() is a public member function of std::set that returns an iterator to the past-the-end element in the set container.

Question 2

I do not know where do these values come from.

Answer

When you wrote:

std::cout << *el.end() << std::endl;//this is undefined behavior

In the above statement you are dereferencing the iterator that was returned by the end() member function.

But note that if we dereference the iterator that was returned by this member function then we get undefined behavior.

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior.


1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.

Jason
  • 36,170
  • 5
  • 26
  • 60