std::pair x std::tuple

por Fabio A. Mazzarino

std::pair já existe há um bom tempo em C++, é a estrutura de dados que recupera os dados de um std::map através de iterators. Já std::tuple foi adicionado no C++11 e apesar de armazenar a mesma coisa é uma classe muito mais completa. Vamos conhecer a diferença entre os dois.

std::pair

std::pair faz parte do include STL utilities, basicamente é uma struct template que acondiciona dois tipos quaisquer de dados, muito utilizado nas containers STL std::map, std::multimap, std::unordered_map e std::unordered_multimap. A situação mais comum é com seus respectivos iterators:

#include <iostream>
#include <map>
int main() {
    std::map<char, unsigned> numbs;
    unsigned n = 1;
    char c = 'a';
    for (; n <= 5; n++, c++)
        numbs[c] = n;
    for (auto it = numbs.begin(); it != numbs.end(); it++)
        std::cout << "numbs[" << it->first << "]: " << it->second << std::endl;
    std::pair<char, unsigned> p = std::make_pair('f', 6);
    std::cout << "numbs[" << p.first << "]: " << p.second << std::endl;
}

Note a utilização do iterator dentro do segundo for. Os métodos begin() e end() irão retornar objetos do tipo std::map<char, unsigned>::iterator. E para acessar os dados da chave e do valor utilizamos os campos do std::pair: first e second. Nas duas últimas linhas é exemplificado um acesso direto à struct, note até a utilização da função std::make_pair().

std::tuple

A grande diferença entre std::pair e std::tuple é a quantidade de elementos possíveis de armazenar: com std::pair somente é possível armazenar dois valores, com std::tuple é possível armazenar quanto for necessário. Por exemplo:

#include <iostream>
#include <tuple>
int main() {
    std::tuple<char, unsigned> duo('g', 7);
    std::tuple<char, unsigned, double> tri('h', 8, 8.0);
    auto quad = std::make_tuple('i', std::string("i"), 9, 9.0);

    std::cout << "duo: [" << std::get<0>(duo) << ", " << std::get<1>(duo) << "]" << std::endl;
    std::cout << "tri: [" << std::get<0>(tri) << ", " << std::get<1>(tri) << ", " << std::get<2>(tri) << "]" << std::endl;
    std::cout << "quad: [" << std::get<0>(quad) << ", " << std::get<1>(quad) << ", " << std::get<2>(quad) << ", " << std::get<3>(quad) << "]" << std::endl;

    char c = '\x0';
    unsigned n = 0;
    std::tie(c, n, std::ignore) = tri;
    std::cout << "c: " << c << std::endl;
    std::cout << "n: " << n << std::endl;
}

No código acima são 3 std::tuples, com dois, três e quatro elementos, consecutivamente. Note que não há a possibilidade de acessar std::get com variáveis, somente com literais. Na segunda parte do código tem um exemplo de como usar std::tie para extrair os dados de um std::tuple.

Quando Utilizar

Um grande uso de std::tuple é para retorno de múltiplos valores, como já abordado em outro post. Com std::tuple, std::make_tuple e std::tie é possível empacotar dois ou mais valores, com std::make_tuple, retornar através de uma função ou método com std::tuple, e em seguida desempacotar utilizando std::tie.