Ir para o conteúdo

Ú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 &x para pegar o endereço e *p para 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:

int a = 1, b = 2;
std::optional<int&> opt = a;
opt = b;  // 问题:这句是什么意思?

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 de int& é 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 legalizado».

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

Comentários