La classe std::tuple e le sue funzioni membro in C++

Jinku Hu 12 ottobre 2023
  1. Usa la funzione std::make_tuple per costruire oggetti std::tuple in C++
  2. Usare un template di funzione per stampare una tupla di qualsiasi dimensione in C++
La classe std::tuple e le sue funzioni membro in C++

Questo articolo dimostrerà più metodi per usare la classe std::tuple e le sue funzioni membro in C++.

Usa la funzione std::make_tuple per costruire oggetti std::tuple in C++

La funzione std::tuple è il modello di classe STL che implementa una raccolta di tipi eterogenei di dimensioni fisse. Generalmente, le tuple sono spesso utilizzate in matematica per denotare la sequenza finita di elementi, ma sono anche associate al prodotto di diversi tipi nei linguaggi di programmazione.

In C++ esiste una classe simile, std::pair, che può memorizzare solo due oggetti eterogenei. La dichiarazione dell’oggetto std::tuple di solito specifica i tipi da cui sarà composta la tupla e l’ordine in cui si accederà. Questi tipi sono specificati come parametri di modello simili a std::pair. Il comando std::tuple può essere inizializzato con diversi costruttori. Tuttavia, in questo caso, utilizziamo la funzione std::make_tuple. Il processo std::make_tuple prende un numero variabile di argomenti e cerca di dedurre i tipi automaticamente. La funzione restituisce l’oggetto std::tuple contenente i valori dati.

Nel seguente programma di esempio, implementiamo una funzione denominata printTupleOfThree, che stampa la tupla di tre valori. Nota che questa funzione modello non può stampare le tuple con un numero diverso di elementi. Il comando printTupleOfThree può essere chiamato su tuple con più di tre elementi, ma produrrà solo i primi tre di essi. Quindi, poiché sarebbe inefficiente definire funzioni sovraccaricate per tuple di dimensioni diverse, è necessario risolvere il problema utilizzando un approccio diverso.

#include <iostream>
#include <tuple>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::tuple;
using std::vector;

template <typename Tuple>
void printTupleOfThree(Tuple t) {
  cout << "(" << std::get<0>(t) << ", " << std::get<1>(t) << ", "
       << std::get<2>(t) << ")" << endl;
}

int main() {
  std::vector<string> v1{"one", "two", "three", "four", "five", "six"};
  std::vector<int> v2 = {1, 2, 3, 4, 5, 6};
  std::vector<float> v3 = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

  auto t1 = std::make_tuple(v1[0], v1[1], v2[0]);
  auto t2 = std::make_tuple(v1[0], v1[1], v2[0], v3[0], v3[1]);
  printTupleOfThree(t1);
  printTupleOfThree(t2);

  return EXIT_SUCCESS;
}

Produzione:

(one, two, 1)(one, two, 2)

Usare un template di funzione per stampare una tupla di qualsiasi dimensione in C++

La soluzione al problema sollevato nella sezione precedente si chiama template variadici. Quest’ultima è la caratteristica dei template di accettare un numero variabile di argomenti. Notate che printTuple è una funzione template variadica che chiama la funzione membro print della classe TuplePrinter. Quest’ultima è un template di classe che definisce una singola funzione membro chiamata print, ma ricordate che ci sono due definizioni della classe TuplePrinter. Questa implementazione è chiamata istanziazione ricorsiva dei template, ed è una tecnica nota nella metaprogrammazione basata sui template.

La metaprogrammazione implica metodi per utilizzare il polimorfismo in fase di compilazione ed è un argomento molto più ampio di quello che questo articolo può gestire. Sarebbe meglio fare riferimento a un libro chiamato C++ Templates - The Complete Guide per una panoramica dettagliata delle funzionalità dei modelli e delle tecniche di metaprogrammazione che li utilizzano. Di conseguenza, otteniamo che la funzione printTuple sia compatibile con oggetti tupla di dimensioni diverse e possiamo stampare tutti i loro membri nel flusso cout.

#include <iostream>
#include <tuple>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::tuple;
using std::vector;

template <class Tuple, std::size_t N>
struct TuplePrinter {
  static void print(const Tuple& t) {
    TuplePrinter<Tuple, N - 1>::print(t);
    std::cout << ", " << std::get<N - 1>(t);
  }
};

template <class Tuple>
struct TuplePrinter<Tuple, 1> {
  static void print(const Tuple& t) { std::cout << std::get<0>(t); }
};

template <class... Args>
void printTuple(const std::tuple<Args...>& t) {
  std::cout << "(";
  TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
  std::cout << ")" << endl;
}

int main() {
  std::vector<string> v1{"one", "two", "three", "four", "five", "six"};
  std::vector<int> v2 = {1, 2, 3, 4, 5, 6};
  std::vector<float> v3 = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

  auto t1 = std::make_tuple(v1[0], v1[1], v3[0]);
  auto t2 = std::make_tuple(v1[0], v1[1], v2[1], v3[0], v3[1]);
  printTuple(t1);
  printTuple(t2);

  return EXIT_SUCCESS;
}

Produzione:

(one, two, 1.1)
(one, two, 2, 1.1, 2.2)
Autore: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook