Introduz C ++ 20 std::common_reference
. Qual é seu propósito? Alguém pode dar um exemplo de como usá-lo?
Introduz C ++ 20 std::common_reference
. Qual é seu propósito? Alguém pode dar um exemplo de como usá-lo?
Respostas:
common_reference
saiu dos meus esforços para apresentar uma conceituação dos iteradores da STL que acomoda iteradores de proxy.
No STL, os iteradores têm dois tipos associados de interesse particular: reference
e value_type
. O primeiro é o tipo de retorno do iterador operator*
e o value_type
é o tipo (não const, não referência) dos elementos da sequência.
Algoritmos genéricos geralmente precisam fazer coisas como esta:
value_type tmp = *it;
... então sabemos que deve haver alguma relação entre esses dois tipos. Para iteradores não proxy, o relacionamento é simples: reference
é sempre value_type
, opcionalmente, const e referência qualificado. As primeiras tentativas de definir o InputIterator
conceito exigiram que a expressão *it
fosse conversível e const value_type &
, para a maioria dos iteradores interessantes, é suficiente.
Eu queria que os iteradores no C ++ 20 fossem mais poderosos que isso. Por exemplo, considere as necessidades de um zip_iterator
que itere duas sequências na etapa de bloqueio. Ao zip_iterator
remover a referência a , você obtém temporariamente pair
os reference
tipos dos dois iteradores . Portanto, zip
ing a vector<int>
e a vector<double>
teriam os seguintes tipos associados:
zip
iterador reference
: pair<int &, double &>
zip
iterador value_type
:pair<int, double>
Como você pode ver, esses dois tipos não estão relacionados entre si simplesmente adicionando a qualificação cv e ref de nível superior. E, no entanto, deixar os dois tipos serem arbitrariamente diferentes parece errado. Claramente, há algum relacionamento aqui. Mas qual é o relacionamento e o que os algoritmos genéricos que operam nos iteradores assumem com segurança sobre os dois tipos?
A resposta no C ++ 20 é que, para qualquer tipo de iterador válido, proxy ou não, os tipos reference &&
e value_type &
compartilham uma referência comum . Em outras palavras, para alguns iteradores, it
existe algum tipo CR
que torna o seguinte bem formado:
void foo(CR) // CR is the common reference for iterator I
{}
void algo( I it, iter_value_t<I> val )
{
foo(val); // OK, lvalue to value_type convertible to CR
foo(*it); // OK, reference convertible to CR
}
CR
é a referência comum. Todos os algoritmos podem confiar no fato de que esse tipo existe e podem ser usados std::common_reference
para computá-lo.
Então, esse é o papel que common_reference
desempenha no STL no C ++ 20. Geralmente, a menos que você esteja escrevendo algoritmos genéricos ou iteradores de proxy, você pode ignorá-lo com segurança. Está lá embaixo, garantindo que seus iteradores cumpram suas obrigações contratuais.
EDIT: O PO também pediu um exemplo. Isso é um pouco artificial, mas imagine que seja C ++ 20 e você receba um intervalo r
de acesso aleatório do tipo R
sobre o qual você não sabe nada e deseja sort
o intervalo.
Imagine ainda que, por algum motivo, você deseja usar uma função de comparação monomórfica, como std::less<T>
. (Talvez você tenha apagado o intervalo de digitação e também precise apagar a função de comparação e passá-la através de um virtual
? Novamente, um trecho.) O que deve T
estar std::less<T>
? Para isso você usaria common_reference
, ou o ajudante iter_common_reference_t
que é implementado em termos dele.
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
Isso garante que funcione, mesmo se o intervalo r
tiver iteradores de proxy.
pair<T&,U&>
e pair<T,U>&
teria uma referência comum, e seria simplesmente pair<T&,U&>
. No entanto, para std::pair
, não há conversão de pair<T,U>&
para, pair<T&,U&>
mesmo que essa conversão seja sólida em princípio. (Esta, aliás, é por isso que não temos uma zip
vista em C ++ 20.)
pair
, em vez de um tipo que poderia ser projetado especificamente para esse fim , com conversões implícitas apropriadas, conforme necessário?
std::pair
; qualquer tipo adequado de par com as conversões apropriadas servirá, e o range-v3 define esse tipo de par. No Comitê, o LEWG não gostou da idéia de adicionar à Biblioteca Padrão um tipo que fosse quase, mas não tanto std::pair
, normativo ou não, sem antes fazer a devida diligência sobre os prós e contras de simplesmente fazer o std::pair
trabalho.
tuple
, pair
, tomato
, to
- MAH
- to
. pair
possui esse recurso interessante que você pode acessar os elementos com .first
e .second
. Ligações estruturadas ajudam com alguns dos constrangimentos de trabalhar com tuple
s, mas não com todos.