Skip to content

Transform iterator assignment change - #2753

Open
danhoeflinger wants to merge 2 commits into
mainfrom
dev/dhoeflin/transform_iterator_assignment_fix
Open

danhoeflinger wants to merge 2 commits into
mainfrom
dev/dhoeflin/transform_iterator_assignment_fix

Conversation

@danhoeflinger

@danhoeflinger danhoeflinger commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

This PR switches transform_iterator copy assignment to use placement new copy construction for the unary functor.
(breaking change)

transform_iterator currently skips assignment of the unary functor if no copy assignment exists by checking constexpr is_copy_assignable_v. (explained here )
However, is_copy_assignable_v only checks for the existence of a copy assignment operator, rather than instantiating it. If it is not well formed, then this breaks compilation.

This originally was necessary to support the copy assignment of transform iterators based upon C++17 lambdas (which are not copy assignable).

This PR provides a possible option for supporting copy assignment for functors lacking copy assignment without this ill-formed copy assignment operator trap.

I would call this a breaking change, as it changes the semantics of copy assignment in a couple ways for a public API tranform_iterator.

  1. In the rare case that (deletion followed by copy construction) vs (copy assignment operator) are defined with different semantics, the copy assignment of transform_iterator will follow copy construction semantics for the unary functor. Note that for device copyable types this cannot be the case as both should be equivalent to bitwise copies and deletion should be a noop.
  2. Copy assignment now always copies the unary functor rather than only when copy assignment exists. This seems very unlikely to cause issues in reality for the same reason that we allowed skipping such cases in the first place.

Both of these are rare corner cases, but technically may affect existing user codes.

implement with placement new (BREAKING CHANGE)

Signed-off-by: Dan Hoeflinger <dan.hoeflinger@intel.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 updates transform_iterator’s copy assignment behavior so the stored unary functor is copied via destruction + copy-construction (rather than relying on _UnaryFunc::operator=), aiming to support functors that are copy-constructible but not copy-assignable (e.g., C++17 lambdas) and avoid ill-formed assignment operator instantiation traps.

Changes:

  • Reworks transform_iterator::operator= to copy the unary functor using explicit destruction followed by placement-new copy construction.
  • Adds a self-assignment guard around the functor-copy portion of the assignment operator.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +523 to 527
if (this != std::addressof(__input))
{
__my_unary_func_ = __input.__my_unary_func_;
__my_unary_func_.~_UnaryFunc();
::new (std::addressof(__my_unary_func_)) _UnaryFunc(__input.__my_unary_func_);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I considered this.

The problem with std::is_assignable_v is that it only checks for the existence of the function, and does not attempt to instantiate it. For template functions where such an assignment is not well-formed this results in a compilation error (this is part of the motivation for this change).

As for the throwing copy resulting in double delete. In this case there is no easy way out. We could implement this as a std::optional or similar equivalent, changing the layout, and marking such an occasion where no constructed functor exists, but then what happens when someone wants to dereference transform_iterator without a constructed functor? I suppose we would need to throw?

Signed-off-by: Dan Hoeflinger <dan.hoeflinger@intel.com>
@danhoeflinger

Copy link
Copy Markdown
Contributor Author

I open this PR for discussion. I'm not sure if it is the right path, but it is an option and solves a real reported issue facing a user. Similar to the current code, it has pros and cons.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants