Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra come usare i puntini di sospensione (...
) con i modelli variadic C++. I puntini di sospensione hanno avuto molti usi in C e C++. Questi includono elenchi di argomenti variabili per le funzioni. La printf()
funzione della libreria di runtime C è uno degli esempi più noti.
Un modello variadic è un modello di classe o funzione che supporta un numero arbitrario di argomenti. Questo meccanismo è particolarmente utile per gli sviluppatori di librerie C++: è possibile applicarlo sia ai modelli di classe che ai modelli di funzione e, di conseguenza, offrire un'ampia gamma di funzionalità e flessibilità indipendenti dai tipi.
Sintassi
I puntini di sospensione vengono usati in due modi dai modelli variadic. A sinistra del nome del parametro, significa un pacchetto di parametri e a destra del nome del parametro, espande i pacchetti di parametri in nomi separati.
Ecco un esempio di base della sintassi di definizione del modello di classe variadic:
template<typename... Arguments> class classname;
Per entrambi i pacchetti di parametri e le espansioni, è possibile aggiungere spazi vuoti intorno ai puntini di sospensione, in base alle preferenze, come illustrato in questo esempio:
template<typename ...Arguments> class classname;
In alternativa, questo esempio:
template<typename ... Arguments> class classname;
Questo articolo usa la convenzione illustrata nel primo esempio (i puntini di sospensione sono collegati a typename
).
Negli esempi precedenti è Arguments
un pacchetto di parametri. La classe classname
può accettare un numero variabile di argomenti, come negli esempi seguenti:
template<typename... Arguments> class vtclass;
vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;
Usando una definizione di modello di classe variadic, è anche possibile richiedere almeno un parametro:
template <typename First, typename... Rest> class classname;
Ecco un esempio di base della sintassi del modello di funzione variadic:
template <typename... Arguments> returntype functionname(Arguments... args);
Il Arguments
pacchetto di parametri viene quindi espanso per l'uso, come illustrato nella sezione successiva.
Altre forme di sintassi del modello di funzione variadic sono possibili, tra cui, a titolo esemplificativo, questi esempi:
template <typename... Arguments> returntype functionname(Arguments&... args);
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);
Gli identificatori come const
sono consentiti anche:
template <typename... Arguments> returntype functionname(const Arguments&... args);
Come per le definizioni di classi modello variadic, è possibile creare funzioni che richiedono almeno un parametro:
template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args);
I modelli variadic usano l'operatore sizeof...()
(non correlato all'operatore precedente sizeof()
):
template<typename... Arguments>
void tfunc(const Arguments&... args)
{
constexpr auto numargs{ sizeof...(Arguments) };
X xobj[numargs]; // array of some previously defined type X
helper_func(xobj, args...);
}
Ulteriori informazioni sulla posizione dei puntini di sospensione
In precedenza, in questo articolo sono stati descritti i puntini di sospensione che definiscono i pacchetti di parametri e le espansioni in questo formato: "a sinistra del nome del parametro, significa un pacchetto di parametri e a destra del nome del parametro, espande i pacchetti di parametri in nomi separati". Anche se tecnicamente vero, può generare confusione nella traduzione nel codice. Tenere in considerazione:
In un template-parameter-list (
template <parameter-list>
)typename...
introduce un pacchetto di parametri del modello.In una clausola parameter-declaration-clause (
func(parameter-list)
), un puntino di sospensione "di primo livello" introduce un pacchetto di parametri di funzione e il posizionamento dei puntini di sospensione è importante:// v1 is NOT a function parameter pack: template <typename... Types> void func1(std::vector<Types...> v1); // v2 IS a function parameter pack: template <typename... Types> void func2(std::vector<Types>... v2);
Quando i puntini di sospensione sono presenti subito dopo un nome di parametro, si ottiene un'espansione del pacchetto di parametri.
Esempio
Un buon modo per illustrare il meccanismo di modello di funzione variadic consiste nell'usarlo in una riscrittura di alcune delle funzionalità di printf
:
#include <iostream>
using namespace std;
void print() {
cout << endl;
}
template <typename T> void print(const T& t) {
cout << t << endl;
}
template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
cout << first << ", ";
print(rest...); // recursive call using pack expansion syntax
}
int main()
{
print(); // calls first overload, outputting only a newline
print(1); // calls second overload
// these call the third overload, the variadic template,
// which uses recursion as needed.
print(10, 20);
print(100, 200, 300);
print("first", 2, "third", 3.14159);
}
Output
1
10, 20
100, 200, 300
first, 2, third, 3.14159
Nota
La maggior parte delle implementazioni che incorporano modelli di funzione variadic usano la ricorsione di alcune forme, ma è leggermente diversa dalla ricorsione tradizionale. La ricorsione tradizionale implica una funzione che chiama se stessa usando la stessa firma. Può essere sottoposto a overload o basato su modelli, ma la stessa firma viene scelta ogni volta. La ricorsione variadic implica la chiamata di un modello di funzione variadic usando numeri di argomenti diversi (quasi sempre decrescente) e quindi contrassegnando una firma diversa ogni volta. È ancora necessario un "caso di base", ma la natura della ricorsione è diversa.