Фундаментальная проблема здесь заключается в семантике : # 1 и # 3 - единственные, где присутствие отличается от отсутствия; # 6 всегда удается вернуть элемент контейнера; а другим всегда удается вернуть значение . Приложение определяет, какие из них вам нужны.
В этом отношении # 1 и # 3 complete : одного из них достаточно для реализации любого другого (учитывая некоторые другие средства добавления элементов для эмуляции # 6). Если можно избежать помех от других потоков, то # 4 и # 5 одинаково мощны: их можно использовать для обнаружения отсутствия, предлагая два разных значения по умолчанию. В качестве альтернативы можно добавить bool contains(int index) const;
, чтобы можно было различать отсутствие (опять же, при необходимости, с внешней синхронизацией).
Однако эти эмуляции (кроме # 2/4/5 из # 1/3) предполагают повторные поиски, которые могут иметь неадекватную производительность. Для некоторых базовых структур данных для обеспечения максимальной производительности могут потребоваться другие операции: например, перемещение элемента из одного индекса в другой без его реконструкции.
Между тем, все эти подходы имеют практические проблемы, по крайней мере, в общем контексте.
- Некоторые эксперты считают, что
logic_error
всегда является ошибкой ; безусловно, исключение в достаточно распространенном случае обходится дорого. Однако здесь можно вернуть ссылку, что очень полезно.
T
должно быть конструктивно (аналогично, но не идентично по умолчанию -конструктивно).
T
должен быть назначаемым (и клиент должен был создать его, возможно, использовать для нескольких вызовов). Неопределенное поведение может быть результатом игнорирования флага (поэтому отметьте его [[nodiscard]]
).
- Два
T
объекта должны быть построены за вызов. По умолчанию можно сделать ссылку, чтобы разрешить возврат ссылки (и поддерживать громоздкую форму обнаружения пропущенных значений), но чтобы избежать разрешения временных аргументов, потребуется перегрузка rvalue-reference (или ограниченный шаблон).
- Как и # 4, это создает два объекта. В контексте шаблона улучшение # 2 в том, что значение-конструктивность требуется, только если значение по умолчанию used .
- Это может, конечно, иметь три варианта, как # 2/4/5. Было бы более полезно, если бы он возвращал ссылку (например,
map::operator[]
), чтобы разрешить изменение (потенциально) нового элемента.
Если T
может быть дорогим для построения (даже из {}
), только # 1 (как используется map::at
) и предложение optional
эффективны; удобно они также завершены. Возможно, самый быстрый вариант - вернуть const T*
, используя нулевой указатель, указывающий на отсутствие. Выбор между ними - это вопрос тонких компромиссов производительности (если только в вашем магазине нет исключений или указателей в целом). Для дешевого T
, # 5 привлекателен, если его семантика достаточна; иначе # 3 может быть лучшим (из-за сходства с if(std::cin >> x)
).