The handle create_wall_timer / rclcpp::create_timer return, and what Timer::SharedPtr names — phase-476 W2.
Two words, trivially copyable, no allocation, and present on every target: the timer's callable lives in the executor arena (phase-476 W2), so there is nothing on the C++ side for a std::shared_ptr to own. That is what freed timer.hpp from <memory> and <functional>.
timer_->cancel(), timer_->reset(), timer_->is_ready() and timer_->time_until_trigger() keep their rclcpp spelling through operator->, which reaches a [TimerOps].
WHAT DIFFERS FROM A shared_ptr, stated because a ported file cannot see it:
- The handle does not OWN the timer. Dropping or overwriting it leaves the timer firing, where upstream destroys a timer when its last
shared_ptr goes. The node owns its timers: destroying the node releases them (phase-476 W0). That matches what this API did before W2, when the node co-owned a heap cell for every timer.
timer_.reset() RELEASES the timer, for every copy at once: the closest thing to upstream's "drop my reference" that a non-counting handle can offer, and the spelling ported code uses to stop a timer for good.
- A copy that outlives a release is SAFE. The handle carries the generation of the slot it was issued for (phase-476 W0), so every operation through a stale copy fails instead of reaching whatever registration took the slot.