HOWTO · C++
Come creare, accedere e stampare valori std::tuple in C++
Scopri come costruire tuple eterogenee con `std::make_tuple`, leggere e aggiornare i loro elementi e stampare tuple di dimensioni diverse in C++.
In questa pagina
Usa std::tuple quando vuoi raggruppare in un unico valore un numero fisso di campi di tipi diversi, come un nome, un identificativo e un punteggio. Questa guida mostra come costruire un valore del genere con std::make_tuple, leggerne e aggiornarne gli elementi con std::get e stampare tutti gli elementi di tuple di dimensioni diverse. Le posizioni degli elementi fanno parte del tipo della tupla: usala quindi per un piccolo record definito, non per una raccolta che cresce durante l’esecuzione.
Che cosa memorizza std::tuple e quando usarlo
std::tuple è un modello di classe che rappresenta una sequenza di valori di dimensione fissa. Ogni posizione può avere un tipo diverso e, in fase di compilazione, sono noti il tipo e l’ordine di tutte le posizioni. Per esempio, una tupla può contenere insieme una stringa, un intero e un valore in virgola mobile. Il tipo è std::tuple: non è una funzione né un comando.
Le posizioni di una tupla non hanno nomi. Per questo è utile per un risultato breve o un raggruppamento locale, ma descrive meno chiaramente i dati quando, in un programma più ampio, i campi devono avere nomi stabili. Usa una struct quando i campi con nome rendono i dati più comprensibili e un contenitore quando il numero di elementi varia.
Costruire una tupla con std::make_tuple
std::make_tuple accetta valori e deduce i tipi corrispondenti degli elementi, evitando di dover specificare un lungo tipo std::tuple<...> nel punto in cui si costruisce la tupla. I valori mantengono l’ordine degli argomenti. Includi <tuple> per usare sia std::tuple sia std::make_tuple; includi <string> quando memorizzi valori std::string. Nel programma completo qui sotto, shortTuple viene costruita a partire da un std::string, un int e un double; std::make_tuple deduce i tipi degli elementi dagli argomenti. I valori vengono copiati o spostati nella tupla in base ai rispettivi tipi. È una costruzione per valore, non un modo per creare una sequenza di dimensione dinamica.
Leggere e aggiornare gli elementi di una tupla con std::get
Usa std::get<I>(value) per accedere all’elemento nella posizione I, dove I è un indice valutato in fase di compilazione che parte da zero. Per una tupla di tre elementi, le posizioni valide sono 0, 1 e 2. Nel programma completo qui sotto, std::get<1>(profile) legge il campo intero in originalId; poi la stessa espressione, sul lato sinistro di un’assegnazione, aggiorna quel campo. La posizione selezionata deve essere valida, quindi non è possibile passare a std::get un indice calcolato solo durante l’esecuzione.
std::tuple non supporta l’indicizzazione con parentesi quadre. Se l’indice deve essere scelto durante l’esecuzione, usa una rappresentazione diversa, per esempio un contenitore omogeneo, oppure definisci una selezione esplicita tra le posizioni note della tupla. La lettura con std::get mantiene il tipo dell’elemento, così il compilatore può verificare assegnazioni e chiamate di funzione rispetto al tipo effettivo del campo.
Stampare ogni elemento con un template variadico
Un template variadico può accettare tuple con numeri diversi di elementi. L’esempio seguente richiede C++17 o una versione successiva perché usa un’espressione fold. Usa std::index_sequence per generare le posizioni valutate in fase di compilazione e un’espressione fold per inviare allo stream ciascun valore corrispondente. Stampa parentesi tonde e separa i valori con virgole; il tipo di ogni elemento deve supportare l’inserimento in std::ostream. Il programma costruisce e aggiorna una tupla, quindi ne stampa altre due di dimensioni diverse. Il suo output è:
Esegui questo esempio:
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
using namespace std;
template <class Tuple, size_t... I>
void printTupleImpl(const Tuple& values, index_sequence<I...>) {
size_t printed = 0;
((cout << (printed++ ? ", " : "") << get<I>(values)), ...);
}
template <class... Values>
void printTuple(const tuple<Values...>& values) {
cout << "(";
printTupleImpl(values, index_sequence_for<Values...>{});
cout << ")\n";
}
int main() {
auto profile = make_tuple(string("Ada"), 7, 9.5);
const int originalId = get<1>(profile);
get<1>(profile) = originalId + 1;
auto shortTuple = make_tuple(string("Ada"), 7, 9.5);
auto longTuple = make_tuple(string("Lin"), 12, 8.25, string("C++"));
printTuple(shortTuple);
printTuple(longTuple);
}
(Ada, 7, 9.5)
(Lin, 12, 8.25, C++)
La funzione ausiliaria espande un’espressione std::get<I> per ogni indice fornito da std::index_sequence_for. Il fold con virgola elabora queste espressioni nell’ordine, perciò la stessa funzione gestisce tuple di entrambe le dimensioni senza richiedere un overload distinto per ogni lunghezza. Gestisce anche una tupla vuota: il fold non inserisce alcun valore e la funzione stampa comunque le parentesi. Anche il valore stampato deve poter essere inserito nello stream; per stampare una tupla che contiene un tipo personalizzato serve un overload appropriato dell’operatore di inserimento nello stream.
Scegliere tra std::tuple e std::pair
std::pair memorizza esattamente due valori, mentre std::tuple può memorizzare un numero fisso qualsiasi di valori, compreso due. Preferisci std::pair quando i dati sono naturalmente composti da due campi correlati, come una chiave e un valore, perché il tipo comunica direttamente questa struttura. Scegli std::tuple quando un gruppo compatto contiene almeno tre valori di tipo diverso o quando un’interfaccia restituisce intenzionalmente più valori insieme.
Per dati usati in molte parti del codice, una struct con campi nominati spesso comunica meglio l’intento rispetto a entrambi i tipi. Per un raggruppamento temporaneo o un valore di ritorno conciso, std::tuple può evitare di definire un tipo separato. In entrambi i casi, mantieni chiaro l’ordine dei campi e accedi alle posizioni della tupla con std::get, così chi legge può capire a quale valore corrisponde ciascun indice.