Eu usei std::tiesem pensar muito nisso. Funciona, então acabei de aceitar que:
auto test(){int a, b;
std::tie(a, b)= std::make_tuple(2,3);// a is now 2, b is now 3return a + b;// 5}
Mas como funciona essa magia negra ? Como um temporário criado pela std::tiemudança ae b? Acho isso mais interessante, pois é um recurso de biblioteca, não um recurso de linguagem, então certamente é algo que podemos implementar e entender.
Para esclarecer o conceito central, vamos reduzi-lo a um exemplo mais básico. Embora std::tieseja útil para funções que retornam (uma tupla de) mais valores, podemos entendê-lo perfeitamente com apenas um valor:
int a;
std::tie(a)= std::make_tuple(24);return a;// 24
Coisas que precisamos saber para seguir em frente:
std::tie constrói e retorna uma tupla de referências.
std::tuple<int>e std::tuple<int&>são 2 classes completamente diferentes, sem nenhuma conexão entre elas, outras que foram geradas a partir do mesmo template std::tuple,.
tupla operator=aceita uma tupla de diferentes tipos (mas mesmo número), onde cada membro é atribuído individualmente - de cppreference :
template<class...UTypes>
tuple&operator=(const tuple<UTypes...>& other );
(3) Para todo i, atribui std::get<i>(other)a std::get<i>(*this).
A próxima etapa é se livrar dessas funções que apenas atrapalham seu caminho, para que possamos transformar nosso código para o seguinte:
int a;
std::tuple<int&>{a}= std::tuple<int>{24};return a;// 24
A próxima etapa é ver exatamente o que acontece dentro dessas estruturas. Para isso, crio 2 tipos de Tsubstituintes std::tuple<int>e Trsubstituintes std::tuple<int&>, reduzidos ao mínimo para nossas operações:
struct T {// substituent for std::tuple<int>int x;};structTr{// substituent for std::tuple<int&>int& xr;autooperator=(const T& other){// std::get<I>(*this) = std::get<I>(other);
xr = other.x;}};auto foo(){int a;Tr{a}= T{24};return a;// 24}
E, finalmente, gosto de me livrar das estruturas todas juntas (bem, não é 100% equivalente, mas é próximo o suficiente para nós e explícito o suficiente para permitir):
Então, basicamente, std::tie(a)inicializa uma referência de membro de dados para a. std::tuple<int>(24)cria um membro de dados com valor 24e a atribuição atribui 24 à referência do membro de dados na primeira estrutura. Mas, como esse membro de dados é um limite de referência a, basicamente atribui 24a a.
O que me incomoda é que estamos chamando o operador de atribuição para um rvalue.
Adam Zahran,
Em esta resposta, ele afirmou que um recipiente não pode segurar uma referência. Por que tuplepoderia conter uma referência?
nn0p
6
@ nn0p std::tuplenão é um contêiner, pelo menos não na terminologia C ++, não é o mesmo que the std::vectore similares. Por exemplo, você não pode iterar com as formas usuais em uma tupla porque ela contém diferentes tipos de objetos.
bolov
@Adam tie (x, y) = make_pair (1,2); na verdade torna-se std :: tie (x, y) .operator = (std :: make_pair (1, 2)), é por isso que "atribuição a um rvalue" funciona XD
Ju Piece
30
Isso não responde a sua pergunta de forma alguma, mas deixe-me postá-la de qualquer maneira porque C ++ 17 está basicamente pronto (com suporte de compilador), então, enquanto você se pergunta como funciona o material desatualizado, provavelmente vale a pena olhar como o atual, e futuro, a versão do C ++ também funciona.
Com o C ++ 17, você pode praticamente ignorar o std::tieque é chamado de ligações estruturadas . Eles fazem o mesmo (bem, não o mesmo , mas eles têm o mesmo efeito), embora você precise digitar menos caracteres, não precisa do suporte da biblioteca e você também tem a capacidade de obter referências, se isso acontecer o que você quer.
(Observe que em C ++ 17 os construtores fazem dedução de argumento, então make_tuplese tornou um tanto supérfluo também.)
int a, b;
std::tie(a, b)= std::make_tuple(2,3);// C++17auto[c, d]= std::make_tuple(4,5);auto[e, f]= std::tuple(6,7);
std::tuple t(8,9);auto&[g, h]= t;// not possible with std::tie
Se essa última linha compilar, fico um pouco preocupado. Parece vincular uma referência a um temporário que é ilegal.
Nir Friedman
3
@Neil Tem que ser uma referência rvalue ou uma referência const lvalue. Você não pode vincular uma referência lvalue a um prvalue (temporário). Embora isso tenha sido uma "extensão" do MSVC por muito tempo.
Nir Friedman
1
Provavelmente também vale a pena mencionar que tie, ao contrário , as ligações estruturadas podem ser usadas dessa forma em tipos que não podem ser construídos por padrão.
Dan
5
Sim, std::tie()é muito menos útil desde C ++ 17, onde ligações estruturadas são geralmente superiores, mas ainda tem usos, incluindo a atribuição de variáveis existentes (não declaradas simultaneamente) e concisamente fazer outras coisas como trocar várias variáveis ou outras coisas que deve atribuir a referências.
tuple
poderia conter uma referência?std::tuple
não é um contêiner, pelo menos não na terminologia C ++, não é o mesmo que thestd::vector
e similares. Por exemplo, você não pode iterar com as formas usuais em uma tupla porque ela contém diferentes tipos de objetos.Isso não responde a sua pergunta de forma alguma, mas deixe-me postá-la de qualquer maneira porque C ++ 17 está basicamente pronto (com suporte de compilador), então, enquanto você se pergunta como funciona o material desatualizado, provavelmente vale a pena olhar como o atual, e futuro, a versão do C ++ também funciona.
Com o C ++ 17, você pode praticamente ignorar o
std::tie
que é chamado de ligações estruturadas . Eles fazem o mesmo (bem, não o mesmo , mas eles têm o mesmo efeito), embora você precise digitar menos caracteres, não precisa do suporte da biblioteca e você também tem a capacidade de obter referências, se isso acontecer o que você quer.(Observe que em C ++ 17 os construtores fazem dedução de argumento, então
make_tuple
se tornou um tanto supérfluo também.)fonte
tie
, ao contrário , as ligações estruturadas podem ser usadas dessa forma em tipos que não podem ser construídos por padrão.std::tie()
é muito menos útil desde C ++ 17, onde ligações estruturadas são geralmente superiores, mas ainda tem usos, incluindo a atribuição de variáveis existentes (não declaradas simultaneamente) e concisamente fazer outras coisas como trocar várias variáveis ou outras coisas que deve atribuir a referências.