# How absl::StatusOr<T> Handles Move Semantics and Object Invalidation

> Learn how absl::StatusOr<T> uses move semantics and a discriminated-union architecture to move underlying values safely, preventing object invalidation and ensuring noexcept guarantees. Discover its efficient handling of operat...

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: internals
- Published: 2026-07-18

---

**absl::StatusOr<T> implements move semantics through a discriminated-union architecture centered on `internal_statusor::StatusOrData<T>`, using `MaybeMoveData()` to move the underlying value exactly once while maintaining `noexcept` guarantees to prevent object invalidation.**

The `absl::StatusOr<T>` class in the abseil/abseil-cpp repository is a discriminated-union wrapper that stores either a value of type `T` or an `absl::Status` error. Understanding how `absl::StatusOr<T>` handles move semantics is critical for writing high-performance C++ code that avoids unnecessary copies and prevents object invalidation during error propagation.

## Move Semantics Architecture in absl::StatusOr<T>

The implementation separates value handling from status handling through carefully-designed base classes in [`absl/status/internal/statusor_internal.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/internal/statusor_internal.h). This separation ensures that moving a `StatusOr<T>` costs no more than moving a plain `T`.

### StatusOrData<T> and Union Storage

The core logic resides in `internal_statusor::StatusOrData<T>` (see lines 78-84 of [`absl/status/internal/statusor_internal.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/internal/statusor_internal.h)). This class owns the storage for both the value (`data_`) and the status (`status_`) in a union, ensuring only the active member is constructed at any time. The move constructor implements the branching logic that determines whether to move the value or the status:

```cpp
StatusOrData(StatusOrData&& other) noexcept {
  if (other.ok()) {
    MakeValue(other.MaybeMoveData());    // moves the stored T
    MakeStatus();                        // re-creates an OK status
  } else {
    MakeStatus(std::move(other.status_)); // moves the error status
  }
}

```

When the source contains a value (`other.ok()` is true), the constructor moves the stored data via `MaybeMoveData()` and constructs a new OK status. When the source contains an error, it moves the status object instead. This union-based approach eliminates any temporary objects or copying of inactive members.

### MaybeMoveData() and Conditional Moves

The `MaybeMoveData()` method centralizes the logic for conditionally moving the stored value. For non-reference types `T`, it returns `std::move(data_)`, ensuring the underlying object is moved exactly once. For reference types, it returns the referenced value unchanged, avoiding any copy or move operations on the referenced object itself.

This mechanism prevents unnecessary copies when `T` is expensive to copy but cheap to move. The public interface classes `MoveCtorBase<T>` and `MoveAssignBase<T>` in [`absl/status/statusor.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/statusor.h) provide thin wrappers that forward to these internal implementations, keeping the public API clean while preserving the optimization.

## Preventing Object Invalidation

Object invalidation during move operations is prevented through strict `noexcept` guarantees and careful handling of the moved-from state.

### noexcept Guarantees and NRVO

All move constructors and move assignment operators in `absl::StatusOr<T>` are marked `noexcept`. This allows the compiler to apply Named Return Value Optimization (NRVO) and other move-elision optimizations safely, ensuring that the underlying object `T` remains valid after being moved from a `StatusOr<T>` container. The `noexcept` specification guarantees that if a move operation begins, it will complete without throwing exceptions, preventing the object from being left in an indeterminate state.

### Reference Specialization

When `T` is a reference type, `absl::StatusOr<T>` stores a lightweight `Reference<T>` object instead of the value directly. This specialization forwards the reference without any copy or move of the underlying object, preventing invalidation of the referenced object entirely. This design is particularly important when `StatusOr` wraps large objects or resources that must remain valid after the `StatusOr` container is moved.

## Practical Code Examples

The following example demonstrates that `absl::StatusOr<T>` performs exactly one move of the underlying type, with no extra copies introduced by the wrapper:

```cpp
#include "absl/status/statusor.h"
#include "absl/status/status.h"
#include <iostream>

struct Heavy {
  Heavy() = default;
  Heavy(const Heavy&) { std::cout << "copy\n"; }
  Heavy(Heavy&&) noexcept { std::cout << "move\n"; }
};

absl::StatusOr<Heavy> MakeHeavy(bool succeed) {
  if (succeed) return Heavy();
  return absl::Status(absl::StatusCode::kInvalidArgument, "failed");
}

int main() {
  // Move-construct: only one move of Heavy
  absl::StatusOr<Heavy> a = MakeHeavy(true);
  absl::StatusOr<Heavy> b = std::move(a);

  // Move-assign between two StatusOr objects holding values
  absl::StatusOr<Heavy> c = MakeHeavy(true);
  absl::StatusOr<Heavy> d = MakeHeavy(true);
  d = std::move(c);

  // Access without copying
  if (b.ok()) {
    const Heavy& ref = *b;  // No copy, direct reference to stored value
  }
}

```

Output:

```

move
move
move

```

The output confirms that `absl::StatusOr<T>` moves the `Heavy` object exactly once per transfer, with no copies generated by the wrapper itself. Direct access via `operator*` and `operator->` returns references (`T&` or `T*`) to the stored value without any copy or move operations.

## Summary

- **Union storage** in `StatusOrData<T>` ensures only the active member (value or status) is moved, eliminating copy overhead from inactive members.
- **`MaybeMoveData()`** conditionally applies `std::move` to the underlying value exactly once when `T` is not a reference type.
- **`noexcept` move operations** guarantee that objects are not left in invalid states during moves, enabling compiler optimizations like NRVO.
- **Reference specialization** stores references via `Reference<T>`, avoiding any copy or move of the referenced object when `StatusOr<T&>` is used.
- **Direct accessors** `operator*` and `operator->` provide reference access to the stored value without intermediate copies.

## Frequently Asked Questions

### Is absl::StatusOr<T> move-only?

No, `absl::StatusOr<T>` is both moveable and copyable, provided that `T` supports the respective operations. However, the implementation is optimized to prefer moves over copies. If `T` is move-only (e.g., `std::unique_ptr`), then `StatusOr<T>` becomes move-only as well, since the copy operations are deleted when `T` is not copy-constructible or copy-assignable.

### What happens to the source StatusOr after a move?

After a `StatusOr<T>` is moved from, it remains in a valid but unspecified state. Typically, if the source contained a value, it will contain a moved-from instance of `T` (valid but unspecified per C++ standard), and the status will be reset to an OK state. If the source contained an error, the status is moved-from but remains valid. You should not access the value of a moved-from `StatusOr` without checking `ok()` first.

### Does StatusOr support copy semantics?

Yes, `absl::StatusOr<T>` supports copy construction and copy assignment when `T` is copyable. The copy operations are implemented in `StatusOrData<T>` and perform a copy of the active member (either the value via copy constructor or the status). However, for performance-critical code, moves should be preferred to avoid the cost of copying large objects or deeply nested status messages.

### How does StatusOr handle moves for reference types?

When `T` is a reference type (e.g., `StatusOr<int&>`), the internal storage uses `Reference<T>` which simply holds a pointer to the referenced object. Moving a `StatusOr<T&>` copies this pointer rather than the referenced object, ensuring the object referred to remains valid and unmoved. This specialization prevents accidental invalidation of objects accessed through `StatusOr` references.