-
Notifications
You must be signed in to change notification settings - Fork 15
Add experiment with reference-wrappers #609
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
Conversation
This comment was marked as off-topic.
This comment was marked as off-topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds exploration tests to demonstrate the interaction between std::reference_wrapper and xyz::indirect, specifically testing swap and move semantics. These tests help clarify how reference wrappers continue to point to the original objects even after the owning indirect instances are swapped or moved.
- Adds two new test cases exploring reference wrapper behavior with
indirect - Updates Bazel build configuration to include the new test target
Reviewed Changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| exploration/reference_wrappers_test.cc | Adds tests demonstrating reference wrapper behavior when indirect objects are swapped or moved |
| exploration/BUILD.bazel | Adds Bazel build target for the new reference wrapper tests |
| MODULE.bazel.lock | Bazel lock file automatically updated with new transitive digest |
| EXPECT_TRUE(b.valueless_after_move()); | ||
| // b-ref refers to the value it referred to before the move. | ||
| EXPECT_EQ(br, 4); // observing the lvalue via operator int&(). | ||
| // Perhaps this is surprising?? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and this means that, to reference_wrapper<T>, the wide contract on an operator T& does not make it safer. One can argue that a hardened, narrow contract operator T& is safer. When having such an operator on indirect<T> itself, it just means "the step to reach an lvalue of the object of T when the storage is managed by indirect<T>" has a narrow contract.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I'd agree that reference-wrapper is unsafe here, but that's expected. I'm not sure that I see the point you're making clearly yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So "reference_wrapper is non-owning and has no null or valueless state" -- not right; reference_wrapper does have a valueless state, it's just that code cannot observe this state.
Imagine this world: the int object x that holds 42 has a narrow contract on evaluation. The contract is violated if you "move out" the value 42 and then bind it to int& -- isn't that just an operator int&() on int? But wait, what if all I want to do is to assign a new value with x = 7? It won't a problem if that x is in fact indirect<int>, because that expression calls indirect<int>::operator=(int&&).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So indirect<T>::operator T&() with a narrow contract is totally safe, extra-safe I'd say. It gets involved in the right contexts, and is not involved when not in the right context...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure what I can do about this: "reference_wrapper does have a valueless state, it's just that code cannot observe this state."
In any case the issues we see seem to be issues with reference_wrapper, not indirect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sentence is not meant to point out any issue with indirect. I mean, the whole motivation for indirect<T>::operator T&() has always been "support incomplete type T as drop-in replacement for T". It was P3902R1 saying that "reference_wrapper can have that because [...], it would be a safety hole in indirect" and I was saying "it's quite the opposite, reference_wrapper's operator T&() has inevitable safety issues, but indirect<T>'s operator T&() is free of that and would add extra safety to T."
Run the new test experiment with
bazel test //exploration:reference_wrappers_test.The experiment can be launched from a devcontainer to save installing dependencies. See the README for details.
References:
indirectiontype