C ++中可变数量的参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1657883/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:47:26  来源:igfitidea点击:

Variable number of arguments in C++?

c++variadic-functions

提问by nunos

How can I write a function that accepts a variable number of arguments? Is this possible, how?

如何编写一个接受可变数量参数的函数?这可能吗,如何?

采纳答案by wilhelmtell

You probably shouldn't, and you can probably do what you want to do in a safer and simpler way. Technically to use variable number of arguments in C you include stdarg.h. From that you'll get the va_listtype as well as three functions that operate on it called va_start(), va_arg()and va_end().

您可能不应该这样做,而且您可能可以以更安全、更简单的方式做您想做的事情。从技术上讲,要在 C 中使用可变数量的参数,您需要包含 stdarg.h。从中您将获得va_list类型以及对其进行操作的三个函数,称为va_start(),va_arg()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;
}

If you ask me, this is a mess. It looks bad, it's unsafe, and it's full of technical details that have nothing to do with what you're conceptually trying to achieve. Instead, consider using overloading or inheritance/polymorphism, builder pattern (as in operator<<()in streams) or default arguments etc. These are all safer: the compiler gets to know more about what you're trying to do so there are more occasions it can stop you before you blow your leg off.

如果你问我,这是一团糟。它看起来很糟糕,不安全,并且充满了与您在概念上试图实现的目标无关的技术细节。相反,请考虑使用重载或继承/多态性、构建器模式(如在operator<<()流中)或默认参数等。这些都更安全:编译器会更多地了解您正在尝试执行的操作,因此它可以在更多情况下停止在你炸掉你的腿之前你。

回答by Shafik Yaghmour

In C++11you have two new options, as the Variadic functionsreference page in the Alternatives sectionstates:

C++11 中,您有两个新选项,正如Alternatives 部分中Variadic 函数参考页面所述:

  • Variadic templates can also be used to create functions that take variable number of arguments. They are often the better choice because they do not impose restrictions on the types of the arguments, do not perform integral and floating-point promotions, and are type safe. (since C++11)
  • If all variable arguments share a common type, a std::initializer_list provides a convenient mechanism (albeit with a different syntax) for accessing variable arguments.
  • 可变参数模板也可用于创建带有可变数量参数的函数。它们通常是更好的选择,因为它们不对参数类型施加限制,不执行整数和浮点提升,并且是类型安全的。(C++11 起)
  • 如果所有变量参数共享一个公共类型,则 std::initializer_list 提供了一种方便的机制(尽管使用不同的语法)来访问变量参数。

Below is an example showing both alternatives (see it live):

下面是一个显示两种替代方案的示例(实时查看):

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

If you are using gccor clangwe can use the PRETTY_FUNCTIONmagic variableto display the type signature of the function which can be helpful in understanding what is going on. For example using:

如果您正在使用gcc或者clang我们可以使用PRETTY_FUNCTION魔术变量来显示函数的类型签名,这有助于理解正在发生的事情。例如使用:

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

would results int following for variadic functions in the example (see it live):

将导致示例中的可变参数函数的 int 以下结果(参见现场直播):

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

In Visual Studio you can use FUNCSIG.

在 Visual Studio 中,您可以使用FUNCSIG

Update Pre C++11

更新 Pre C++11

Pre C++11the alternative for std::initializer_listwould be std::vectoror one of the other standard containers:

C++11 之前std::initializer_list的替代方案是std::vector或其他标准容器之一

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

and the alternative for variadic templateswould be variadic functionsalthough they are not type-safeand in general error prone and can be unsafe to usebut the only other potential alternative would be to use default arguments, although that has limited use. The example below is a modified version of the sample code in the linked reference:

可变参数模板的替代方案将是可变参数函数,尽管它们不是类型安全的,并且通常容易出错并且使用起来可能不安全,但唯一的其他潜在替代方案是使用默认参数,尽管使用有限。下面的示例是链接参考中示例代码的修改版本:

#include <iostream>
#include <string>
#include <cstdarg>

void simple_printf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    while (*fmt != '
template<class... Args>
void print(Args... args)
{
    (std::cout << ... << args) << "\n";
}
print(1, ':', " Hello", ',', " ", "World!");
') { 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 ; }

Using variadic functionsalso comes with restrictions in the arguments you can pass which is detailed in the draft C++ standardin section 5.2.2Function callparagraph 7:

使用可变参数函数也会限制您可以传递的参数,这在函数调用7节中的C++ 标准草案中有详细说明:5.2.2

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.7). The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined. [...]

当给定的参数没有参数时,参数的传递方式是接收函数可以通过调用 va_arg (18.7) 来获取参数的值。对参数表达式执行左值到右值 (4.1)、数组到指针 (4.2) 和函数到指针 (4.3) 的标准转换。在这些转换之后,如果参数没有算术、枚举、指针、成员指针或类类型,则程序格式错误。如果参数具有非 POD 类类型(第 9 条),则行为未定义。[...]

回答by YSC

A C++17 solution: full type safety + nice calling syntax

C++17 解决方案:完整类型安全 + 漂亮的调用语法

Since the introduction of variadic templates in C++11 and fold expressions in C++17, it is possible to define a template-function which, at the caller site, is callable as if it was a varidic function but with the advantages to:

由于在 C++11 中引入了可变参数模板并在 C++17 中引入了折叠表达式,因此可以定义一个模板函数,它在调用者站点上可以像可变参数函数一样调用,但具有以下优点: :

  • be strongly type safe;
  • work without the run-time information of the number of arguments, or without the usage of a "stop" argument.
  • 是强类型安全的;
  • 在没有参数数量的运行时信息或没有使用“停止”参数的情况下工作。

Here is an example for mixed argument types

这是混合参数类型的示例

#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!");
                                                                                              ^

And another with enforced type match for all arguments:

另一个强制类型匹配所有参数:

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

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

More information:

更多信息:

  1. Variadic templates, also known as parameter pack Parameter pack(since C++11) - cppreference.com.
  2. Fold expressions fold expression(since C++17) - cppreference.com.
  3. See a full program demonstrationon coliru.
  1. 可变参数模板,也称为参数包参数包(C++11 起) - cppreference.com
  2. 折叠表达式折叠表达式(C++17 起) - cppreference.com
  3. 查看有关coliru的完整程序演示

回答by Markus Zancolò

in c++11 you can do:

在 C++11 中,您可以执行以下操作:

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

list initializer FTW!

列表初始值设定项 FTW!

回答by Omnifarious

In C++11 there is a way to do variable argument templates which lead to a really elegant and type safe way to have variable argument functions. Bjarne himself gives a nice example of printf using variable argument templatesin the C++11FAQ.

在 C++11 中有一种方法来做可变参数模板,这导致了一种非常优雅且类型安全的方式来拥有可变参数函数。Bjarne 本人在C++11FAQ 中给出了使用可变参数模板printf 的一个很好的例子。

Personally, I consider this so elegant that I wouldn't even bother with a variable argument function in C++ until that compiler has support for C++11 variable argument templates.

就我个人而言,我认为这非常优雅,以至于在编译器支持 C++11 可变参数模板之前,我什至不会打扰 C++ 中的可变参数函数。

回答by Will

C-style variadic functions are supported in C++.

C++ 支持 C 风格的可变参数函数。

However, most C++ libraries use an alternative idiom e.g. whereas the 'c' printffunction takes variable arguments the c++ coutobject uses <<overloading which addresses type safety and ADTs (perhaps at the cost of implementation simplicity).

然而,大多数 C++ 库使用另一种习惯用法,例如,当'c' printf函数采用可变参数时,c++ cout对象使用<<重载来解决类型安全和 ADT(可能以实现简单性为代价)。

回答by Francesco

Apart from varargs or overloading, you could consider to aggregate your arguments in a std::vector or other containers (std::map for example). Something like this:

除了可变参数或重载之外,您还可以考虑在 std::vector 或其他容器(例如 std::map)中聚合您的参数。像这样的东西:

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

// other code...

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

In this way you would gain type safety and the logical meaning of these variadic arguments would be apparent.

通过这种方式,您将获得类型安全性,并且这些可变参数的逻辑含义将显而易见。

Surely this approach can have performance issues but you should not worry about them unless you are sure that you cannot pay the price. It is a sort of a a "Pythonic" approach to c++ ...

当然,这种方法可能会出现性能问题,但除非您确定无法付出代价,否则您不必担心这些问题。这是 C++ 的一种“Pythonic”方法......

回答by Dave Van den Eynde

The only way is through the use of C style variable arguments, as described here. Note that this is not a recommended practice, as it's not typesafe and error-prone.

唯一的办法是通过使用C风格的变量参数,如所描述这里。请注意,这不是推荐的做法,因为它不是类型安全的且容易出错。

回答by Zoli

There is no standard C++ way to do this without resorting to C-style varargs (...).

如果不求助于 C 风格的可变参数 ( ...),就没有标准的 C++ 方法可以做到这一点。

There are of course default arguments that sort of "look" like variable number of arguments depending on the context:

当然,有一些默认参数看起来像可变数量的参数,具体取决于上下文:

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

All four function calls call myfuncwith varying number of arguments. If none are given, the default arguments are used. Note however, that you can only omit trailing arguments. There is no way, for example to omit iand give only j.

所有四个函数调用都myfunc使用不同数量的参数调用。如果没有给出,则使用默认参数。但是请注意,您只能省略尾随参数。没有办法,例如省略i和只给j

回答by Kieveli

It's possible you want overloading or default parameters - define the same function with defaulted parameters:

您可能想要重载或默认参数 - 使用默认参数定义相同的函数:

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

This will allow you to call the method with one of four different calls:

这将允许您使用四种不同的调用之一来调用该方法:

##代码##

... or you could be looking for the v_args calling conventions from C.

...或者您可能正在寻找来自 C 的 v_args 调用约定。