---
title: "Better Name for Better Lookups in P3091"
document: P4139R3
date: 2026-06-11
audience: LEWG
reply-to:
  - "Nathan Myers <ncm@cantrip.org>"
  - "Pablo Halpern <phalpern@halpernwightsoftware.com>"
---

# Abstract

[[P3091R5]](https://wg21.link/p3091r5), forwarded to LWG for C++29, adds member functions `get` to associative containers in the Standard Library. These member functions work like `operator[]` or `at`, but return an `optional<T&>` value instead of a simple reference; when lookup fails, they return the empty object. While the feature was strongly favored, support for the name `get` yielded only a weak consensus, with opposition to the name focusing on the fact that elsewhere in the Standard Library, `get` calls (with one exception) cannot fail or take not-strictly-bounded time.

In hope of making the C++26 cutoff, the naming discussion was curtailed and the name in P3091 was retained when that paper was forwarded in Sofia. Since that time, even the author of P3091 has reconsidered the implications of the name `get`. As LWG has not yet taken up the paper (which is now on track for C++29) we present an analysis of the status quo and propose alternative names for the desired functionality.

# Change Log

- R2 - Pre-Brno Mailing, 2026-05: Add survey of existing usage of `get`, comparisons among proposed alternatives, and wording.
- R1 - Post-Croydon Mailing, 2026-04: Add Pablo Halpern as co-author; identify correct (C++29) target; add to other indexable containers; suggest when operations should be no-throw.
- R0 - Croydon 2026-03, posted during the meeting.

# Motivation

## Current Usage of get in the Standard Library

In the current Working Draft, the Standard Library defines functions `get` in `<array>`, `<complex>`, `<format>`, `<functional>`, `<future>`, `<memory>`, `<ranges>`, `<tuple>`, `<utility>`, and `<variant>` (neglecting I/O and locale, where `get` just complements `put`) and, notably, *none* of the growable containers. Every `get` function in these headers has the following properties:

- It returns a reference to or copy of a subobject, performing a monadic unwrapping.
- Its returned value is already present, admitting no failure. Even when `shared_ptr<>::get` or `unique_ptr<>::get` return `nullptr`, that is the actual value wrapped.
- When it returns without throwing, the operation was successful. In most cases, it cannot fail.
- It does essentially no computation to obtain the value. Most know the location at compile time; the least disciplined, `format_args::get(i)`, does an indexed array lookup. `future::get` may block or throw, but if the computation completed successfully, it returns immediately. (Pablo wishes it known that he considers this last property of little interest.)

The library has accumulated an assortment of `try_*xxxxx*` members that share the feature of returning in the case of failure, but yield a variety of result types, some upon failure presenting an iterator adjacent to where an element *would have* been inserted or erased, or `end()`, or a default-constructed iterator in a pair with a boolean flag. They generally cost less on failure than the non-`try` alternative, where there is one. Now that returning an `optional<T&>` is possible, it is favored for these.

For decades before those, associative containers have had `at(key)` that throws on a lookup failure, and `operator[](key)` that inserts a default-initialized value and returns a reference to it, but also might throw if that insertion fails. That these return an element value reference makes them close kin to the feature adopted from [[P3091R5]](https://wg21.link/p3091r5).

## Conclusions Drawn from Current Usage

We conclude that a `get` function returning `optional` after an unbounded search would make it unique among uses of `get` in the library, and so would be inconsistent and confusing. (Pablo again emphasizes that he does not consider search time relevant here.)

The name of the desired operation should reflect that the lookup may take variable time and could return without finding the value being sought; existing instances of `get` in the Library forbid both.

P3091 offered names `get_optional` and `lookup_optional`, which failed to evoke enthusiasm in Sofia; and `lookup`, which had supporters, but failed to reach consensus. Interest at the time was focused on semantics, with discussion of names felt by many to be a distraction. The only criterion mentioned for a good name was that it be short, which seems to the authors secondary to descriptive clarity and consistency.

The goal of this paper is to propose a short list of names which meet the criteria of being short, accurately descriptive, and consistent (or at least not inconsistent) with the names of other value-or-reference-returning functions that report failure via a non-throwing path.

# Proposed Change

This is a multipart proposal. The parts can be polled separately.

## A Quick Review of P3091

[[P3091R5]](https://wg21.link/p3091r5) proposed a new member function for associative containers returning an `optional<mapped_type&>` that identifies an object if the key is found, or otherwise is empty. A typical use might look like:

```cpp
  if (auto maybe = container.xxxxx(key)) {
    use(*maybe);
  }
```

or

```cpp
  use(container.xxxxx(key).value_or(mapped{}));
```

## Part 1: Change *map-like*::get to a different name in P3091

It is common to downplay the importance of naming, but bad names do real harm. Naming discussions should not be an exercise in throwing a bunch of names in the air and voting based on gut reactions. We propose that LEWG choose among the three alternatives below. When polling the alternatives, we should pay attention not only to the name that receives the most positive votes, but the opposition to any specific name.

| Name | Discussion |
| --- | --- |
| `try_at` | Implies `at`-like functionality, replacing throwing with `optional<T&>` as the failure channel. |
| `lookup` | Simple and does not imply success. (Pablo’s preference) |
| `try_lookup` | The `try_` prefix hints that if lookup fails it does not throw, consistent with other instances such as `inplace_vector<T>::try_push`. |

Recall that the operation does not throw unless a user-supplied function object it calls (comparison, hash, etc) throws.

This change is targeted for C++29. Note that P3091 has not yet been voted into the working paper. This proposal would not change anything in the C++26 DIS.

## Part 2: Add a Similar Member Function to Random-access Sequence Containers

The R2 version of this paper additionally proposed adding a similar feature to random-access sequence containers, but such a change failed to garner consensus in LEWG in Brno:

> POLL: Add a `lookup` member function to the random-access sequence containers:

| SF | F | N | A | SA |
| --- | --- | --- | --- | --- |
| 0 | 8 | 8 | 4 | 4 |

# Alternatives considered

The name `lookup_optional` meets the stated criteria, except for its length. If there is sufficient interest, it might be considered as well. The name `get_optional` mentioned in P3091 is not proposed, for reasons that should be clear.

# Wording

Generate a new revision of P3091, replacing `get` with `lookup`, including in the feature-test macro.

# References

[N5032] Thomas Köppe. 2025-12-15. Working Draft, Standard for
Programming Language C++.

https://wg21.link/n5032

[P3091R5] Pablo Halpern. 2025-10-03. Better lookups for `map`,
`unordered_map`, and `flat_map`.

https://wg21.link/p3091r5

---

1. All citations to the Standard are to working draft N5032 unless otherwise specified.↩︎
