Última atualização:2026-10-12
C++17 optional_ref¶
Este post relata o blog do foonathan optional_ref discussion; o original é a melhor discussão sobre por que std::optional<T&> (uma referência opcional) nunca entrou no padrão. O tema voltou à mesa às vésperas do C++26, então vale percorrer os argumentos centrais.
1. O problema: por que std::optional não compila?¶
std::optional<int>, std::optional<std::string> funcionam sem problemas, mas std::optional<int&> falha na compilação direto. O motivo é meio «legado histórico»: quando o padrão foi elaborado não houve consenso sobre a sua semântica de atribuição, então o padrão simplesmente afirma que o comportamento é indefinido quando T é um tipo referência.
E o que existe de tão difícil numa «referência que pode estar vazia»? O autor destilou quatro perguntas:
(1) optional<T&> é a mesma coisa que um ponteiro?
(2) realmente precisamos de optional<T&>?
(3) ao atribuir, deve fazer rebind (religar ao novo objeto) ou assign-through (escrever no objeto apontado)?
(4) ele deveria ter operador de atribuição afinal?
As respostas tl;dr do autor: não, não, rebind, e não deveria. Vejamos por quê.
2. Uma referência opcional não é um ponteiro¶
Intuitivamente, uma «referência possivelmente vazia» é um ponteiro, mas o autor aponta uma diferença mais essencial:
- a criação e o acesso de
T&são ambos implícitos: com a referência em mãos, usa-se diretamente, sem desreferenciar; - a criação e o acesso de
T*são ambos explícitos: precisa de&xpara pegar o endereço e*ppara desreferenciar.
Essa diferença de explicitude tem valor: um ponteiro lembra você de que «aqui pode haver vazio, pode haver problemas de aliasing». Fazer de optional<T&> um «ponteiro mais cômodo» com conversões implícitas em todo lugar joga fora justamente essa camada de proteção. A conclusão do autor: se você quer expressar «pode estar vazio», um T* explícito é até melhor documentação.
3. rebind ou assign-through¶
Este é o nó central de toda a discussão:
Duas leituras:
- rebind: opt agora se liga a b (a semântica de alias das referências;
int& ra = a; ra = b;atribui b a a… espere, a atribuição deint&é justamente assign-through!) - assign-through: escrever o valor de b em a (a semântica de uma referência comum
T&).
Aí está a contradição: se a referência opcional quiser alinhar-se à semântica de T&, a atribuição deveria ser assign-through; mas um optional mutável (que pode ser repreenchido com outro objeto) intuitivamente exige rebind. As duas semânticas são razoáveis, o comité não chegou a um resultado, e assim optional<T&> nunca foi legalizado.
Na prática, quando se quer semântica de rebind, a solução da comunidade é criar o próprio optional_ref<T>: internamente guarda um T*, e atribuir é trocar de ponteiro — semântica cristalina. A proposta de std::optional_ref do C++26 segue o mesmo caminho.
4. Resumo¶
(1) optional<T&> não é açúcar sintático de ponteiro; os dois têm uma divisão essencial de papéis na explicitude;
(2) o que o travou foi a ambiguidade da semântica de atribuição: tanto rebind quanto assign-through fazem sentido;
(3) quando é precisa uma «referência anulável», um ponteiro explícito ou um optional_ref são escolhas mais seguras do que um «optional
Para fechar, uma questão: value() e *opt de optional<T&> deveriam retornar T& ou const T&? E se também quiser expressar a semântica de «capturado como const», como desenhar o parâmetro do template?
Tópicos relacionados¶
- Atributo [[nodiscard]] no C++17 | C++那些事
- C++17 std::string_view | C++那些事
- Recursos de memória PMR no C++17 | C++那些事
- Dezessete funcionalidades avançadas do C++17 | C++那些事
- Dedução de argumentos de template de classe CTAD no C++17 | C++那些事
- C++17 std::variant | C++那些事
- Vinculações estruturadas no C++17 | C++那些事
- Dedução com auto em templates no C++17 | C++那些事
- Templates de variável no C++17 | C++那些事