Por que o uso de tuplas no C ++ não é mais comum?

124

Por que ninguém parece usar tuplas em C ++, nem a Boost Tuple Library ou a biblioteca padrão para TR1? Eu li muito código C ++ e, muito raramente, vejo o uso de tuplas, mas muitas vezes vejo muitos lugares em que as tuplas resolveriam muitos problemas (geralmente retornando vários valores das funções).

As tuplas permitem que você faça todos os tipos de coisas legais como esta:

tie(a,b) = make_tuple(b,a); //swap a and b

Isso é certamente melhor que isso:

temp=a;
a=b;
b=temp;

Claro que você sempre pode fazer isso:

swap(a,b);

Mas e se você quiser girar três valores? Você pode fazer isso com tuplas:

tie(a,b,c) = make_tuple(b,c,a);

As tuplas também facilitam muito o retorno de várias variáveis ​​de uma função, o que provavelmente é um caso muito mais comum do que trocar valores. Usar referências para retornar valores certamente não é muito elegante.

Existem grandes desvantagens nas tuplas em que não estou pensando? Caso contrário, por que eles raramente são usados? Eles são mais lentos? Ou será que as pessoas não estão acostumadas com elas? É uma boa ideia usar tuplas?

Zifre
fonte
17
+1 para truque troca tupla inteligente :)
kizzx2
10
a = a ^ b; b = a ^ b; a = a ^ b;
Gerardo Marset
3
As tuplas da IMO são convenientes em idiomas de digitação fracos ou em idiomas em que são estruturas nativas. Por exemplo, em Python ou PHP, eles facilitam a vida, enquanto em C ++ há muita digitação (para construí-la a partir do modelo) e poucos benefícios.
doc
4
Um comentário ao OP: Penso que a resposta atualmente aceita já está obsoleta a ponto de estar factualmente errada. Você pode reconsiderar a escolha da resposta aceita.
ulidtko
8
@GerardoMarset Você está falando sério?
TheSaint

Respostas:

43

Porque ainda não é padrão. Qualquer coisa fora do padrão tem um obstáculo muito maior. Pedaços de Boost se tornaram populares porque os programadores clamavam por eles. (hash_map vem à mente). Mas enquanto a tupla é útil, não é uma vitória tão esmagadora e clara que as pessoas se preocupam com ela.

Alan De Smet
fonte
1
As pessoas parecem usar outras partes do Boost como loucas. Embora certamente os mapas de hash sejam muito mais úteis em geral do que as tuplas.
Zifre
Não sei as especificidades do que você está vendo, mas acho que as partes que as pessoas estão usando como loucas são características que realmente queriam. Assim (novamente, adivinhando) a popularidade do mapa de hash, o ponteiro contado e similares. A tupla é útil, mas não é algo que salta para preencher um buraco. A recompensa não é óbvia. Com que frequência você precisa girar exatamente N objetos? (Em vez de precisar girar vetores arbitrariamente longos). E as pessoas estão acostumadas a passar valores de retorno por referência ou retornar classes ou estruturas pequenas.
1955 Alan De Smet
20
É actualy uma parte do C ++ 11 padrão agora: en.cppreference.com/w/cpp/utility/tuple
Roman Susi
124

Uma resposta cínica é que muitas pessoas programam em C ++, mas não entendem e / ou usam a funcionalidade de nível superior. Às vezes é porque eles não são permitidos, mas muitos simplesmente não tentam (ou mesmo entendem).

Como um exemplo sem estímulo: quantas pessoas usam a funcionalidade encontrada <algorithm>?

Em outras palavras, muitos programadores de C ++ são simplesmente programadores de C usando compiladores de C ++, e talvez std::vectore std::list. Essa é uma razão pela qual o uso de boost::tuplenão é mais comum.

Trey Jackson
fonte
18
-1 de mim porque os programadores de C ++ não são tão burros quanto esta resposta os faz soar.
user541686
5
@Mehrdad Tendo analisado muitos códigos C ++, comerciais e não, lendo toneladas de material C ++, acho bastante seguro dizer que uma porção muito grande de desenvolvedores de "C ++" são apenas desenvolvedores de C que não conseguiram Compilador C. Modelos, por exemplo, estão quase totalmente ausentes da maioria dos materiais (algo que aprendi a amar muito). Hackers de macro estranhos são comuns e o espaço para nome é muito subutilizado.
5/10
5
Resposta sem sentido. Se alguém precisar deles, eles os alcançarão. Eles não são necessários; então eles não são usados. Dizer que eles não são usados ​​porque não são facilmente entendidos é ruim.
Michael Chourdakis
9
Comentário de Michael Nonsense. Nada é necessário na programação depois de obter um subconjunto completo de Turing de uma linguagem. A falta de uso certamente não implica que todos entendam as construções C ++ de nível superior e optem por não usá-las.
Trey Jackson
4
NUNCA tive a necessidade de std :: tupla fora da metaprogramação de modelos variados. Não havia sentido na vida em que me sentava com uma cara triste pensando "Se eu tivesse essas tuplas". De fato, quando olho para as tuplas, penso "Para que diabos alguém precisaria delas (eu não me consideraria sã)". Pessoas fora da metaprogramação parecem usá-las como "Estrutura Anônima", o que é muito feio e indica uma forte ausência de qualidade e manutenção de código em suas cabeças.
TheSaint
23

A sintaxe da tupla em C ++ pode ser um pouco mais detalhada do que a maioria das pessoas gostaria.

Considerar:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

Portanto, se você quiser fazer uso extensivo de tuplas, obtenha typedefs de tupla em todos os lugares ou nomes de tipos irritantemente longos em todos os lugares. Eu gosto de tuplas. Eu os uso quando necessário. Mas geralmente é limitado a algumas situações, como um índice de elemento N ou ao usar multimaps para amarrar os pares de iteradores de intervalo. E é geralmente em um escopo muito limitado.

É tudo muito feio e com aparência de hacker quando comparado a algo como Haskell ou Python. Quando o C ++ 0x chegar aqui e recebermos as tuplas de palavra-chave 'auto', elas parecerão muito mais atraentes.

A utilidade das tuplas é inversamente proporcional ao número de pressionamentos de teclas necessários para declará-los, compactá-los e descompactá-los.

user21714
fonte
A maioria das pessoas faz "usando o aumento do espaço para nome"; e não precisa digitar boost ::. Não acho que digitar tupla seja um grande problema. Dito isto, acho que você tem razão. auto pode fazer muito mais pessoas começarem a usar tuplas.
Zifre 12/05/09
2
@ Zifre: o problema é que você não deve "usar o espaço para nome X" em um arquivo de cabeçalho, porque força a poluição do espaço para nome e subverte os espaços para nome.
Sr. Fooz
1
Ah, sim, eu esqueço os cabeçalhos. Mas dentro do código do programa, você não precisa se preocupar com isso. E uma vez que tenhamos C ++ 0x, podemos usar auto, o que deve eliminar muita digitação.
Zifre 14/05/09
19
Sou apenas eu? Eu não acho que salvar digitando os 7 caracteres de "boost ::" é o que ele estava se referindo, mas os outros 33 caracteres . Isso é muita digitação de nome de classe, especialmente se esses também tiverem escopo de espaço para nome. Tome boost :: tupla <std :: string, std :: set <std :: string>, std :: vector <My :: Scoped :: LongishTypeName>> como um exemplo ridículo.
Ogre Psalm33
10

Para mim, é hábito, mãos para baixo: tuplas não resolvem novos problemas para mim, apenas alguns que eu já consigo lidar bem. A troca de valores ainda parece mais fácil à moda antiga - e, mais importante, eu realmente não penso em como trocar "melhor". É bom o suficiente como está.

Pessoalmente, não acho que as tuplas sejam uma ótima solução para retornar vários valores - parece um trabalho para structs.

ojrac
fonte
4
'Realmente não penso em como trocar "melhor".' - Quando escrevo código, escrevo bugs. Reduzir a complexidade do código reduz o número de bugs que eu escrevo. Eu odeio criar os mesmos erros repetidamente. Sim, eu penso em como <greve> trocar </> código melhor . Menos partes móveis (LOC, variáveis ​​temporárias, identificadores para erros de digitação), código mais legível; Good Code.
23/09/11
Aceita. Uma classe envolvida em ponteiro automático ou ponteiro inteligente é salva em tipo. Eu usei tuplas uma vez, mas depois reescrevi o código usando classes. retValue.state é mais claro que retValue.get <0> ().
Valentin Heinitz
1
@sehe: Escrever código melhor e mais legível também é meu objetivo. Adicionar mais tipos de sintaxe tem um custo, e eu não acredito que "uma melhor troca" justifique a sobrecarga mental de pensar em ainda mais tipos de sintaxe para cada linha de código que você lê.
ojrac
8

Mas e se você quiser girar três valores?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

OK, então com 4 valores etc., eventualmente, a n-tupla se torna menos código que n-1 swaps. E com a troca padrão, são 6 atribuições em vez das 4 que você teria se implementasse um modelo de três ciclos, embora eu esperasse que o compilador resolvesse isso para tipos simples.

Você pode criar cenários em que os swaps são pesados ​​ou inapropriados, por exemplo:

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

é um pouco estranho descompactar.

O ponto é, no entanto, existem maneiras conhecidas de lidar com as situações mais comuns para as quais as tuplas são boas e, portanto, não há grande urgência em aceitar as tuplas. Se nada mais, não estou confiante de que:

tie(a,b,c) = make_tuple(b,c,a);

não faz 6 cópias, tornando-o totalmente inadequado para alguns tipos (as coleções são as mais óbvias). Fique à vontade para me convencer de que as tuplas são uma boa ideia para tipos "grandes", dizendo que não é assim :-)

Para retornar vários valores, as tuplas são perfeitas se os valores forem de tipos incompatíveis, mas algumas pessoas não gostam deles se for possível para o chamador obtê-las na ordem errada. Algumas pessoas não gostam de vários valores de retorno e não querem incentivar o uso delas, tornando-as mais fáceis. Algumas pessoas simplesmente preferem estruturas nomeadas para parâmetros de entrada e saída e provavelmente não poderiam ser persuadidas com um taco de beisebol a usar tuplas. Não há explicação para o gosto.

Steve Jessop
fonte
1
Você definitivamente não gostaria de trocar vetores por tuplas. Eu acho que trocar três elementos é certamente mais claro com tuplas do que com duas swaps. Quanto a vários valores de retorno, os parâmetros out são ruins, as estruturas são de digitação extra e, definitivamente, há casos em que vários valores de retorno são necessários.
Zifre
sei por que você usa tuplas (e eu sei por que eu as usaria se surgisse a ocasião, embora eu ache que nunca). Estou supondo por que outras pessoas não as usam, mesmo que tenham consciência delas. por exemplo, porque eles não concordam com "fora params são maus" ...
Steve Jessop
Você sabe se podemos substituir "tie (a, b, c) = make_tuple (b, c, a);" por "empate (a, b, c) = empate (b, c, a);" ?
Rexxar
2
Um empate (bem, um nível tecnicamente) é uma tupla feita com referências não const. Não consigo encontrar a documentação do impulso que diz o que garante o operador = e o construtor de cópia para empate / tupla quando algumas das referências envolvidas têm o mesmo referendo. Mas é isso que você precisa saber. A implementação ingênua de operador = claramente poderia ir muito errado ...
Steve Jessop
1
@Steve: E como os laços têm tudo a ver com impedir cópias (eles devem funcionar para tipos não copiáveis; observe que o LHS pode ser algo totalmente diferente), de fato, tudo deve dar muito errado (pense em objetos de classe que não sejam POD). Imagine como você escreveria a mesma lógica sem usar temporários.
23/09/11
7

Como muitas pessoas apontaram, as tuplas simplesmente não são tão úteis quanto outros recursos.

  1. Os truques de troca e rotação são apenas truques. Eles são totalmente confusos para aqueles que nunca os viram antes e, como quase todo mundo, esses truques são apenas más práticas de engenharia de software.

  2. Retornar vários valores usando tuplas é muito menos auto-documentado do que as alternativas - retornar tipos nomeados ou usar referências nomeadas. Sem essa auto-documentação, é fácil confundir a ordem dos valores retornados, se eles são mutuamente conversíveis e não são mais sábios.

igorlord
fonte
6

Nem todo mundo pode usar o impulso, e o TR1 ainda não está amplamente disponível.

Brian Neal
fonte
3
Muitas pessoas usam o Boost. Essas pessoas também podem estar usando tuplas.
Zifre 12/05/09
3
Você perguntou por que as pessoas não as usam e eu dei uma resposta.
13139 Brian Neal
2
Para o eleitor inativo: por acaso trabalho em um local onde é politicamente impossível usar o boost, e mesmo nessa data, a cadeia de ferramentas do compilador que usamos (para um sistema incorporado) não tem suporte ao TR1 / C ++ 11.
27612 Brian Neal
5

Ao usar o C ++ em sistemas incorporados, a atração das bibliotecas do Boost fica complexa. Eles se juntam, então o tamanho da biblioteca aumenta. Você retorna estruturas de dados ou usa a passagem de parâmetros em vez de tuplas. Ao retornar tuplas em Python, a estrutura de dados está na ordem e no tipo dos valores retornados, mas isso não é explícito.

DanM
fonte
5

Você raramente os vê porque o código bem projetado geralmente não precisa deles - não existem muitos casos em que o uso de uma estrutura anônima é superior ao uso de uma nomeada. Como tudo que uma tupla realmente representa é uma estrutura anônima, a maioria dos codificadores, na maioria das situações, apenas acompanha a coisa real.

Digamos que temos uma função "f" em que um retorno da tupla pode fazer sentido. Como regra geral, essas funções geralmente são complicadas o suficiente para que possam falhar.

Se "f" PODE falhar, você precisará retornar um status; afinal, você não quer que os chamadores tenham que inspecionar todos os parâmetros para detectar falhas. "f" provavelmente se encaixa no padrão:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

Isso não é bonito, mas veja como a alternativa é feia. Observe que ainda preciso de um valor de status, mas o código não é mais legível e nem mais curto. Provavelmente também é mais lento, pois incorre no custo de 1 cópia com a tupla.

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

Outra desvantagem significativa está oculta aqui - com "ReturnInts", posso adicionar o retorno de alter "f" modificando "ReturnInts" SEM ALTERAR A INTERFACE de "f". A solução de tupla não oferece esse recurso crítico, o que a torna a resposta inferior para qualquer código de biblioteca.

Zack Yezek
fonte
1
Exceções tornam essa interface muito mais limpa.
David Stone
Para ser justo (e extremamente atrasado para a festa), você pode facilitar a legibilidade definindo using std::tuple;e apenas usando tupleo código.
Alrekr
2
Usar tupletorna o código menos legível, não mais. Atualmente, a maioria dos códigos possui um número muito grande de símbolos - a visão std::tupledeixa claro aos olhos exatamente o que é.
22630 Tom Tom Swirly
3

Certamente, as tuplas podem ser úteis, mas, como mencionado, há um pouco de sobrecarga e uma ou duas barreiras que você precisa passar antes que possa usá-las.

Se o seu programa encontrar consistentemente locais onde você precisa retornar vários valores ou trocar vários valores, pode valer a pena seguir a rota da tupla, mas, caso contrário, às vezes é apenas mais fácil fazer as coisas da maneira clássica.

De um modo geral, nem todo mundo já possui o Boost instalado, e eu certamente não passaria pelo trabalho de fazer o download e configurar meus diretórios de inclusão para trabalhar com ele apenas por suas instalações de tupla. Acho que você descobrirá que as pessoas que já usam o Boost têm maior probabilidade de encontrar usos de tupla em seus programas do que os usuários que não são do Boost, e os migrantes de outros idiomas (Python vem à mente) têm mais probabilidade de simplesmente ficarem chateados com a falta de tuplas em C ++ do que explorar métodos para adicionar suporte à tupla.

Zac
fonte
1

Como um armazenamento de dados std::tupletem as piores características de um structe um array; todo o acesso é a enésima posição, mas não se pode iterar tupleusando umfor loop.

Portanto, se os elementos no tuplesão conceitualmente uma matriz, usarei uma matriz e se os elementos não forem conceitualmente uma matriz, uma estrutura (que nomeou elementos) é mais sustentável. ( a.lastnameé mais explicativo do questd::get<1>(a) ).

Isso deixa a transformação mencionada pelo OP como a única via viável para tuplas.

Doron
fonte
0

Sinto que muitos usam Boost.Any e Boost.Variant (com alguma engenharia) em vez do Boost.Tuple.

dirkgently
fonte
Por que você trocaria a digitação estática eficiente por algo assim?
Zifre 12/05/09
O Boost.Variant é totalmente seguro para o tipo.
User21714 12/12/2009
1
Ops, sim, é seguro, mas digita em tempo de execução.
Zifre 12/05/09
5
Não vejo como a Tupla pode ser substituída Qualquer / Variante. Eles não fazem a mesma coisa.
precisa saber é o seguinte
1
@ Zifre Não posso falar pelo autor, mas acho que a implicação aqui é usá-los junto com outros tipos de contêineres.
Tim Seguine