[binary_sensor] Use const T* instead of T current{} in set_new_state

Avoids requiring T to be default-constructible, which would break
future instantiations with non-default-constructible state types.

Capture old_state before set_state_value() since the pointer aliases
subclass storage that gets overwritten.
This commit is contained in:
J. Nick Koston
2026-03-22 18:40:00 -10:00
parent cd5b19ab0d
commit eaf38c10cc
+10 -11
View File
@@ -347,27 +347,26 @@ template<typename T> class StatefulEntityBase : public EntityBase {
virtual bool set_new_state(const optional<T> &new_state) {
// Access flags_ directly to avoid function call overhead in this hot path
bool had_state = this->flags_.has_state;
// Cache get_state() result to avoid calling the virtual method twice
T current{};
if (had_state)
current = this->get_state();
// Use pointer to avoid requiring T to be default-constructible
const T *current = had_state ? &this->get_state() : nullptr;
if (new_state.has_value()) {
if (had_state && current == new_state.value())
if (current != nullptr && *current == new_state.value())
return false; // same value, no change
} else if (!had_state) {
return false; // already invalidated, no change
}
// State changed — update storage before firing callbacks so callback code
// can inspect the entity's current state via get_state()/has_state()
// Capture old_state before set_state_value — current pointer aliases subclass storage
bool has_full_cbs = !this->full_state_callbacks_.empty();
optional<T> old_state;
if (has_full_cbs)
old_state = current != nullptr ? optional<T>(*current) : nullopt;
// Update storage before firing callbacks so callback code can inspect current state
this->flags_.has_state = new_state.has_value();
if (new_state.has_value()) {
this->set_state_value(new_state.value());
}
// Only construct old_state and call full_state_callbacks when callbacks are registered
if (!this->full_state_callbacks_.empty()) {
optional<T> old_state = had_state ? optional<T>(current) : nullopt;
if (has_full_cbs)
this->full_state_callbacks_.call(old_state, new_state);
}
// had_state first: on every change except the first, skips the virtual call
if (new_state.has_value() && (had_state || this->get_trigger_on_initial_state()))
this->state_callbacks_.call(new_state.value());