Pergunta

Como posso escrever uma função que aceita um número variável de argumentos? Isso é possível, como?

Foi útil?

Solução

Você provavelmente não deveria, e provavelmente você pode fazer o que você quer fazer de forma mais segura e mais simples. Tecnicamente usar número variável de argumentos em C você incluir stdarg.h. Do que você vai ter o tipo va_list, bem como três funções que operam em que chamou va_start(), va_arg() e 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;
}

Se você me perguntar, esta é uma bagunça. Parece ruim, não é seguro, e é cheio de detalhes técnicos que não têm nada a ver com o que você está conceitualmente tentando alcançar. Em vez disso, considere o uso de sobrecarga ou herança / polimorfismo, padrão do construtor (como no operator<<() em córregos) ou argumentos padrão etc. Estes são todos mais seguros: o compilador fica a saber mais sobre o que você está tentando fazer isso há mais ocasiões que puder pará-lo antes de explodir sua perna.

Outras dicas

Em C ++ 11 você tem duas novas opções, como o funções variádicos página de referência no seção Alternativas estados:

    templates
  • variádicos também pode ser usado para criar funções que recebem um número variável de argumentos. Eles são muitas vezes a escolha melhor, porque eles não impõem restrições à os tipos dos argumentos, não execute promoções integrais e de ponto flutuante, e são do tipo seguro. (Desde C ++ 11)
  • Se todos os argumentos variáveis ??compartilhar um tipo comum, um std :: initializer_list fornece uma mecanismo conveniente (embora com uma sintaxe diferente) para aceder aos argumentos variáveis.

Abaixo está um exemplo mostrando ambas as alternativas ( vê-lo viver ):

#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 } ) ;
} 

Se você estiver usando gcc ou clang podemos usar a PRETTY_FUNCTION variável mágica para exibir a assinatura de tipo da função que pode ser útil na compreensão do que está acontecendo. Por exemplo usando:

std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;

faria resultados int seguinte para funções variádicos no exemplo ( vê-lo viver ):

void func(T, Args...) [T = int, Args = <double, char, std::basic_string<char>>]: 1
void func(T, Args...) [T = double, Args = <char, std::basic_string<char>>]: 2.5
void func(T, Args...) [T = char, Args = <std::basic_string<char>>]: a
void func(T) [T = std::basic_string<char>]: Hello

No Visual Studio você pode usar FUNCSIG .

Atualização Pré C ++ 11

Pré C ++ 11 a alternativa para std :: initializer_list seria std :: vector ou um dos outros < a href = "http://en.cppreference.com/w/cpp/container" rel = "noreferrer"> contentores normais :

#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 ) ;
}

e a alternativa para variádica modelos seria funções variádicos embora eles não são e em href="http://robertseacord.sys-con.com/node/158825/mobile" erro geral de tipo seguro propensos e pode ser inseguro para usar mas a única outra alternativa potencial seria a utilização de argumentos padrão , embora que tem uso limitado. O exemplo a seguir é uma versão modificada do código de exemplo na referência vinculado:

#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 ;
} 

Usando funções variádicos também vem com restrições nos argumentos que você pode passar o que está detalhado no projecto C ++ padrão na seção 5.2.2 chamada de Função parágrafo 7 :

Quando não há parâmetros para um determinado argumento, o argumento é passado de forma tal que a função de recepção pode obter o valor do argumento invocando va_arg (18,7). O rvalue Ivalue-a-(4.1), matriz-se ponteiro (4.2), e função-para-ponteiro (4.3) conversões padrão são realizadas na expressão argumento. Após essas conversões, se o argumento não tem aritmética, enumeração, ponteiro, ponteiro para membro ou tipo de classe, o programa está mal-formado. Se o argumento tem um tipo de classe não-POD (cláusula 9), o comportamento é indefinido. [...]

em c ++ 11 você pode fazer:

void foo(const std::list<std::string> & myArguments) {
   //do whatever you want, with all the convenience of lists
}

foo({"arg1","arg2"});

lista initializer FTW!

Em C ++ 11 há uma maneira de fazer modelos de argumentos variáveis ??que levam a uma forma segura realmente elegante e digite ter funções de argumentos variáveis. -se Bjarne dá um exemplo agradável de printf usando modelos de argumentos variáveis ?? na C ++ 11FAQ .

Pessoalmente, considero este tão elegante que eu não teria sequer se preocupar com uma função de argumentos variável em C ++ até que compilador tem suporte para C ++ 11 modelos de argumentos variáveis.

funções variádicos de estilo C são suportados em C ++.

No entanto, a maioria das bibliotecas C ++ usar uma alternativa, por exemplo, idioma enquanto que a função 'c' printf leva argumentos variáveis ??do c++ cout objeto usa << sobrecarga que a segurança e ADTs (talvez no custo de implementação simplicidade) Tipo de endereços.

Além de varargs ou sobrecarga, você poderia considerar a agregar seus argumentos em um std :: vector ou outros recipientes (std :: mapa, por exemplo). Algo parecido com isto:

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);

Desta forma você ganharia segurança de tipo e do significado lógico desses argumentos variádicos seria aparente.

Certamente esta abordagem pode ter problemas de desempenho, mas você não deve se preocupar com eles, a menos que tenha certeza que você não pode pagar o preço. É um tipo de abordagem a um "Pythonic" para c ++ ...

A C ++ 17 solução: completa segurança de tipo + agradável chamada sintaxe

Desde a introdução de modelos de variádicos em C ++ 11 e dobra expressões em C ++ 17, é possível definir uma função-molde que, no local do receptor, é que pode ser chamado como se fosse uma função varidic mas com as vantagens para:

  • ser digitar fortemente seguro;
  • trabalho sem as informações de tempo de execução do número de argumentos, ou sem o uso de um argumento "stop".

Aqui está um exemplo para os tipos de argumento misto

template<class... Args>
void print(Args... args)
{
    (std::cout << ... << args) << "\n";
}
print(1, ':', " Hello", ',', " ", "World!");

E outro com imposta jogo tipo para todos os argumentos:

#include <type_traits> // enable_if, conjuction

template<class Head, class... Tail>
using are_same = std::conjunction<std::is_same<Head, Tail>...>;

template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
void print_same_type(Head head, Tail... tail)
{
    std::cout << head;
    (std::cout << ... << tail) << "\n";
}
print_same_type("2: ", "Hello, ", "World!");   // OK
print_same_type(3, ": ", "Hello, ", "World!"); // no matching function for call to 'print_same_type(int, const char [3], const char [8], const char [7])'
                                               // print_same_type(3, ": ", "Hello, ", "World!");
                                                                                              ^

Mais informações:

  1. modelos variádicos, também conhecido como parâmetro pacote Parâmetro pacote (desde C ++ 11) -. cppreference.com
  2. expressão vezes (uma vez que C ++ 17) - cppreference.com .
  3. completa programa de demonstração em coliru.

A única maneira é através do uso de argumentos variáveis ??de estilo C, como descrito aqui . Note-se que esta não é uma prática recomendada, pois não é typesafe e propenso a erros.

Não há padrão C ++ maneira de fazer isso sem recorrer a C-style varargs (...).

Há de argumentos padrão do curso esse tipo de "look" como número variável de argumentos, dependendo do contexto:

void myfunc( int i = 0, int j = 1, int k = 2 );

// other code...

myfunc();
myfunc( 2 );
myfunc( 2, 1 );
myfunc( 2, 1, 0 );

Todos os quatro chamadas de função chamar myfunc com número variável de argumentos. Se nenhum for dado, os argumentos padrão são usados. Nota no entanto, que você só pode omitir arrastando argumentos. Não há nenhuma maneira, por exemplo, para i omitir e dão apenas j.

É possível que você quer sobrecarregar ou parâmetros padrão - defina a mesma função com parâmetros inadimplentes:

void doStuff( int a, double termstator = 1.0, bool useFlag = true )
{
   // stuff
}

void doStuff( double std_termstator )
{
   // assume the user always wants '1' for the a param
   return doStuff( 1, std_termstator );
}

Isto irá permitir que você chamar o método com um dos quatro chamadas diferentes:

doStuff( 1 );
doStuff( 2, 2.5 );
doStuff( 1, 1.0, false );
doStuff( 6.72 );

... ou você poderia estar a olhar para os v_args convenções de chamada de C.

Como já foi dito, C-style varargs. Mas você também pode fazer algo semelhante com argumentos padrão.

Se você conhece a gama de número de argumentos que serão fornecidos, você sempre pode usar alguma sobrecarga de funções, como

f(int a)
    {int res=a; return res;}
f(int a, int b)
    {int res=a+b; return res;}

e assim por diante ...

int fun(int n_args, ...) {
   int *p = &n_args; 
   int s = sizeof(int);
   p += s + s - 1;
   for(int i = 0; i < n_args; i++) {
     printf("A1 %d!\n", *p);
     p += 2;
   }
}

versão Plain

Usando modelos variádicos, exemplo para reproduzir console.log como visto em JavaScript:

Console console;
console.log("bunch", "of", "arguments");
console.warn("or some numbers:", 1, 2, 3);
console.error("just a prank", "bro");

Nome do ficheiro, por exemplo, js_console.h:

#include <iostream>
#include <utility>

class Console {
protected:
    template <typename T>
    void log_argument(T t) {
        std::cout << t << " ";
    }
public:
    template <typename... Args>
    void log(Args&&... args) {
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }

    template <typename... Args>
    void warn(Args&&... args) {
        cout << "WARNING: ";
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }

    template <typename... Args>
    void error(Args&&... args) {
        cout << "ERROR: ";
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }
};

Nós também poderia usar um initializer_list se todos os argumentos são const e do mesmo tipo

É possível agora ... usando o impulso qualquer e modelos Neste caso, tipo de argumentos podem ser misturados

#include <boost/any.hpp>
#include <iostream>

#include <vector>
using boost::any_cast;

template <typename T, typename... Types> 
void Alert(T var1,Types... var2) 
{ 

    std::vector<boost::any> a(  {var1,var2...});

    for (int i = 0; i < a.size();i++)
    {

    if (a[i].type() == typeid(int))
    {
        std::cout << "int "  << boost::any_cast<int> (a[i]) << std::endl;
    }
    if (a[i].type() == typeid(double))
    {
        std::cout << "double "  << boost::any_cast<double> (a[i]) << std::endl;
    }
    if (a[i].type() == typeid(const char*))
    {
        std::cout << "char* " << boost::any_cast<const char*> (a[i]) <<std::endl;
    }
    // etc
    }

} 


void main()
{
    Alert("something",0,0,0.3);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top