-
Notifications
You must be signed in to change notification settings - Fork 446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unexpected behavior in generate with return by reference #807
Comments
This looks like a memory bug to me. I get the same output that you get on debug mode, but if I run it with O3 I get: Returning vector of size 4
-> Obtained vector of size 4
Returning vector of size 4
-> Obtained vector of size 4
Returning vector of size 4
==========
Returning vector of size 4
-> Obtained vector of size 18446708891870198896
Returning vector of size 4
-> Obtained vector of size 4
Returning vector of size 4 Notice that on my machine the code produces
The lambda is called once on the construction of the view, and the result is stored within the view. When an iterator is incremented, a new result is stored in the view. When you write: int main(int /*argc*/, char ** /*argv*/) {
auto ps = perms();
std::cout << " -> Obtained vector of size " << ranges::begin(ps)->size() << '\n';
std::cout << " -> Obtained vector of size " << ranges::begin(ps)->size() << '\n';
} which prints: Returning vector of size 4
-> Obtained vector of size 4
-> Obtained vector of size 4 dereferencing the iterators does not need to check whether there is a value in the view already, and if there isn't, create one, because the view always contains one value. Also that example creates two iterators into the view, and both return the same value (the one that is cached inside the view). If one increments one of the iterators, that advances the range, so that dereferencing the other would also return the new value. When you do a So I would say this is expected. Whether it's the right call, I don't know. This is an That would mean, however, that while the code I posted above would call the lambda two times, dereferencing the two independent iterators to the front of the range would return two different values. And that code like this: int main(int /*argc*/, char ** /*argv*/) {
auto ps = perms();
auto it = ranges::begin(ps);
std::cout << " -> Obtained vector of size " << it>size() << '\n';
std::cout << " -> Obtained vector of size " << it->size() << '\n';
} would also call the lambda only twice, but dereferencing the same iterator twice would return a different value each time... Whether dereferencing an input iterator is allowed to advance the range or not, I don't know. Dereferencing the same iterator is allowed to return two different values, but that does not mean that the dereference operation is allowed to advance the range. It might be possible to have your cake and eat it too but every approach I can think of turns overly complicated quickly. |
OK, thanks a lot for the reply, I believe I see the point. I would have expected In any case, the undefined behavior remains. |
Wait, the initial bug I reported was about the undefined behavior, not about the number of times the function was called (that was incidental). As far as I am aware the main issue remains, and is not a duplicate of #819 - though it might well be fixed at the same time |
This is a tricksy bug. The problem happens when the generator function returns an internal reference. The fix isn't hard: don't call the generate function eagerly on construction. Defer until |
The following code gives a weird output:
On both clang (OSX, trunk) and gcc (linux, 7.4) the output I obtain is this:
Notice the
0
as the size of the first returned vector for the second version. I would not expect a difference between the two options here, is there something I am missing?Related question, I would have expected the lambda passed to
generate
to be called 2 rather than 3 times with that code, are 3 calls the expected behavior?The text was updated successfully, but these errors were encountered: