Comment puis-je écrire une fonction qui accepte un nombre variable d’arguments ? Est-ce possible, comment ?
Réponses
Trop de publicités?En C++11 , vous avez deux options, comme les Variadic fonctions de la page de référence dans les solutions de rechange section membres:
- Variadic templates peuvent également être utilisés pour créer des fonctions qui prennent un nombre variable de les arguments. Ils sont souvent le meilleur choix, car ils n'imposent pas de restrictions sur les types d'arguments, ne pas faire partie intégrante et à virgule flottante promotions, et sont de type sécuritaire. (depuis C++11)
- Si tous les variable d'arguments de la part d'un type commun, un std::initializer_list fournit un mécanisme pratique (mais avec une syntaxe différente) pour accéder à la variable d'arguments.
Ci-dessous est un exemple montrant deux alternatives:
#include <iostream>
#include <string>
#include <initializer_list>
template <typename T>
void func(T t)
{
std::cout << t << std::endl ;
}
template<typename T, typename... Args>
void func(T t, Args... args) // recursive variadic function
{
std::cout << t <<std::endl ;
func(args...) ;
}
template <class T>
void func2( std::initializer_list<T> list )
{
for( auto elem : list )
{
std::cout << elem << std::endl ;
}
}
int main()
{
std::string
str1( "Hello" ),
str2( "world" );
func(1,2.5,'a',str1);
func2( {10, 20, 30, 40 }) ;
func2( {str1, str2 } ) ;
}
Mise À Jour Avant C++11
Avant C++11 , l'alternative pour std::initializer_list serait std::vector ou l'un des autres conteneurs standard:
#include <iostream>
#include <string>
#include <vector>
template <class T>
void func1( std::vector<T> vec )
{
for( typename std::vector<T>::iterator iter = vec.begin(); iter != vec.end(); ++iter )
{
std::cout << *iter << std::endl ;
}
}
int main()
{
int arr1[] = {10, 20, 30, 40} ;
std::string arr2[] = { "hello", "world" } ;
std::vector<int> v1( arr1, arr1+4 ) ;
std::vector<std::string> v2( arr2, arr2+2 ) ;
func1( v1 ) ;
func1( v2 ) ;
}
et l'alternative pour les variadic templates serait variadic fonctions , bien qu'ils ne sont pas de type sécurisé et, en général, les risques d'erreurs et peut être dangereux à utiliser , mais la seule autre alternative serait d'utiliser des arguments par défaut, bien que l'usage est limité. L'exemple ci-dessous est une version modifiée de l'exemple de code dans les liens de référence:
#include <iostream>
#include <string>
#include <cstdarg>
void simple_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
while (*fmt != '\0') {
if (*fmt == 'd') {
int i = va_arg(args, int);
std::cout << i << '\n';
} else if (*fmt == 's') {
char * s = va_arg(args, char*);
std::cout << s << '\n';
}
++fmt;
}
va_end(args);
}
int main()
{
std::string
str1( "Hello" ),
str2( "world" );
simple_printf("dddd", 10, 20, 30, 40 );
simple_printf("ss", str1.c_str(), str2.c_str() );
return 0 ;
}
À l'aide de variadic fonctions est également livré avec des restrictions dans les arguments que vous pouvez passer qui est détaillé dans le projet de norme C++ dans la section 5.2.2
appel de Fonction, paragraphe 7:
Quand il n'y a pas de paramètre pour un argument donné, l'argument est passé de telle manière que la fonction de réception peut obtenir la valeur de l'argument en invoquant va_arg (18.7). La lvalue-à-rvalue (4.1), tableau de pointeur (4.2), et la fonction de pointeur (4.3) standard conversions sont effectuées sur l'argument de l'expression. Après ces conversions, si l'argument n'est pas de l'arithmétique, de l'énumération, pointeur, pointeur de membre, ou le type de classe, le programme est mal formé. Si l'argument est un non-POD type de classe (article 9), le comportement est indéfini. [...]
Vous ne devriez probablement pas, et vous pouvez probablement faire ce que vous voulez faire dans un endroit plus sûr et plus simple. Techniquement d'utiliser un nombre variable d'arguments en C, vous comprennent stdarg.h. À partir de cela, vous aurez l' va_list
type ainsi que de trois fonctions qui opèrent sur elle appelait va_start()
, va_arg()
et va_end()
.
#include<stdarg.h>
int maxof(int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
int max = va_arg(ap, int);
for(int i = 2; i <= n_args; i++) {
int a = va_arg(ap, int);
if(a > max) max = a;
}
va_end(ap);
return max;
}
Si vous me demandez, c'est un gâchis. Il est mauvais, il est dangereux, et c'est plein de détails techniques qui n'ont rien à voir avec ce que vous êtes sur le plan conceptuel essayez d'atteindre. Au lieu de cela, envisager l'utilisation d'une surcharge ou d'un héritage/le polymorphisme, générateur de motif (en operator<<()
dans les cours d'eau) ou des arguments par défaut etc. Ce sont toutes plus sûr: le compilateur arrive à en savoir plus à propos de ce que vous essayez de le faire il y a plus d'occasions, il peut vous arrêter avant que vous soufflez vos amputer la jambe.
Fonctions de style C variadique sont pris en charge en C++.
Cependant, la plupart des bibliothèques C++ utilisent un langage alternatif par exemple alors que le fonction accepte des arguments variables la
objets utilisations `` surcharge quelles adresses de type sécurité et ADTs (peut-être au détriment de la simplicité de mise en œuvre).
En dehors de varargs ou de surcharge, vous pourriez envisager de regrouper les arguments dans un std::vector ou autres récipients (std::map par exemple). Quelque chose comme ceci:
template <typename T> void f(std::vector<T> const&);
std::vector<int> my_args;
my_args.push_back(1);
my_args.push_back(2);
f(my_args);
De cette façon, vous permettrait de gagner du type de la sécurité et de la logique de la signification de ces variadic arguments seraient apparente.
Sûrement, cette approche peut avoir des problèmes de performances, mais vous ne devez pas s'inquiéter à leur sujet, sauf si vous êtes sûr que vous ne pouvez pas payer le prix. C'est une sorte de "Pythonic" approche du c++ ...
Le seul moyen est par l’utilisation d’arguments variables de style C, comme décrit ici. Notez que ce n’est pas une pratique recommandée, car il n'est pas de type sécurisé et sujette à erreur.