C++ C/C++;

C++ C/C++;,c++,c,function,arguments,C++,C,Function,Arguments,我想知道在C/C++语言中是否可以以键值形式将参数传递给函数。 例如,在python中,您可以执行以下操作: def some_function(arg0 = "default_value", arg1): # (...) value1 = "passed_value" some_function(arg1 = value1) 因此,C中的替代代码可以如下所示: void some_function(char *arg0 = "def

我想知道在C/C++语言中是否可以以键值形式将参数传递给函数。 例如,在python中,您可以执行以下操作:

def some_function(arg0 = "default_value", arg1):
    # (...)

value1 = "passed_value"
some_function(arg1 = value1)
因此,C中的替代代码可以如下所示:

void some_function(char *arg0 = "default_value", char *arg1)
{
    ;
}

int main()
{
    char *value1 = "passed_value";
    some_function(arg1 = value1);
    return(0);
}
myclass x("bob", 3);                     // positional
myclass y(_index = 12, _name = "sally"); // named
myclass z("june");                       // positional/defaulted
因此,在某些函数中使用的参数是:

arg0=“默认值”

arg1=“传递的值”


<0 > P/> P> >不,不能在C或C++中按名称传递参数。


这两种语言都支持函数中尾随参数的默认参数。也就是说,函数的任何参数都可能具有默认值,但找到第一个默认值后的所有后续参数也必须具有默认值

例如,以下各项均有效:

void f(int a, int b = 0);
void g(double a = 1, double b = 2);
void h(int a = 3, int b = 2, int c = 1, int d = 0);
void i(float a, float b, float c = 1, float d = 2);
但以下各项均无效:

void j(int a = 1, int b);
void k(int a, int b = 1, int c);
void l(int a = 2, int b = 1, int c);

理由很简单:因为在C和C++中不能按名称传递参数,所以知道使用默认值的哪些参数并使用传递值的唯一方法是将传递的值按顺序分配给参数,然后使用剩余的默认值(按顺序)。 因此,使用上述函数,以下调用是有效的:

f(0); // calls f(0, 1);
g(); // calls g(1,2);
g(10); // calls g(10,2);
h(); // calls h(3,2,1,0);
h(1,2); // calls h(1,2,1,0);

你可以用C++仿真这个:

struct params {
   string foo_;
   double bar_;
   short  xxx_;
   params() : foo_("123"), bar_(3.1415), xxx_(42) {} // default parameters
   params& foo(string s) {foo_=s;return *this;}
   params& bar(double x) {bar_=x;return *this;}
   params& xxx(short x) {xxx_=x;return *this;}
};

void some_function(params const & p);

int main() {
   some_function(params().bar(99.9).xxx(23));
}
但我觉得这不值得努力。锅炉板太多


如果我没记错的话,Stroustrup的书《C++的设计和发展》中有一节讨论了“命名参数”的特性请求。结论大致是:这不是一个好主意。如果需要详细信息,请查看它。

C中不支持命名参数,但您可以使用变量函数模拟命名参数(尽管您不支持类型安全):

#包括
无效做某事
{
int baz=7;/*“baz”参数*/
常量char*xyz=“xyz”/*“xyz”参数*/
/*解析命名参数*/
va_列表ap;
va_启动(ap、foo);
对于(;;){
const char*key=va_arg(ap,char*);
if(key==NULL){
/*终结者*/
打破
}否则如果(strcmp(键,“baz”)==0){
baz=va_arg(ap,int);
}否则如果(strcmp(键,“xyz”)==0){
xyz=va_arg(ap,char*);
}否则{
/*处理错误*/
}
}
va_端(ap);
/*做些有用的事*/
}
做某事(1,空);//没有命名参数
做某事(2,“baz”,12,空);//baz=12
做某事(3,“xyz”,“foobaz”,空);//xyz=“foobaz”
做某事(4,“baz”,12,“xyz”,“foobaz”,空);//baz=12,xyz=“foobaz”
只需记住以NULL结束可选参数列表。您还可以使用一些枚举作为键,而不是字符串

例如,该技术在GTK+中使用:


仅限C++的解决方案: 您可以使用boost::any、std::map和std::string来创建具有posible参数的对象

#include <iostream>
#include <map>
#include <string>

#include "boost/any.hpp"

using namespace std;

void variableArguments(map<string, boost::any> arguments){
    cout << ">>> variableArguments begins" << endl;

    if(arguments.find("intParameter") != arguments.end()){
    cout << "Found int parameter: " 
         << boost::any_cast<int>(arguments["intParameter"]) << endl;
    }

    if(arguments.find("stringParameter") != arguments.end()){
    cout << "Found string parameter: " 
         << boost::any_cast<string>(arguments["stringParameter"]) << endl;
    }

    cout << "<<< variableArguments ends" << endl;

}

int main(int argc, char *argv[])
{

    map<string, boost::any> argMap;
    argMap["intParameter"] = 5;
    argMap["stringParameter"] = string("MyString");

    variableArguments(argMap);

    argMap.erase(argMap.find("intParameter"));

    variableArguments(argMap);

    return 0;
}
#包括
#包括
#包括
#包括“boost/any.hpp”
使用名称空间std;
无效变量参数(映射参数){

Cava

这在香草C或C++中是不可用的。然而,有一个C++升压库,让你为你写的函数做这个:借用一个例子,它的用法有点像:

void some_function(char *arg0 = "default_value", char *arg1)
{
    ;
}

int main()
{
    char *value1 = "passed_value";
    some_function(arg1 = value1);
    return(0);
}
myclass x("bob", 3);                     // positional
myclass y(_index = 12, _name = "sally"); // named
myclass z("june");                       // positional/defaulted

不过,为您的函数实现这一点看起来确实有些复杂。您可能会认为这不值得努力。

这里有一个使用复合文字和可变宏的C99解决方案:

#include <stdio.h>

#define some_func(...) some_func_((struct some_func_args_){ __VA_ARGS__ })

struct some_func_args_
{
    const char *arg1;
    const char *arg2;
};

static void some_func_(struct some_func_args_ args)
{
    if(!args.arg1) args.arg1 = "default";
    printf("---\narg1 = %s\narg2 = %s\n", args.arg1, args.arg2);
}

int main(void)
{
    some_func("foo", "bar");
    some_func(.arg1 = "spam");
    some_func(.arg2 = "eggs");
    return 0;
}
#包括
#定义一些函数(…)一些函数((结构一些函数参数){{uuuuuuu VA_uuu参数})
构造一些函数参数_
{
常量字符*arg1;
常量字符*arg2;
};
静态void some函数(struct some函数参数)
{
如果(!args.arg1)args.arg1=“默认”;
printf(“--\narg1=%s\narg2=%s\n”,args.arg1,args.arg2);
}
内部主(空)
{
一些func(“foo”、“bar”);
一些函数(.arg1=“垃圾邮件”);
一些_func(.arg2=“鸡蛋”);
返回0;
}
以非笨拙的方式获取返回值更加困难

这将允许您做一些非常愚蠢的事情(我讨厌),例如为参数指定默认值:

#define call_foo(...) \
   do { \
      int first = default_first; \
      int second; \
      int third;  \
      (__VA_ARGS__); \
      foo(first, second, third); \
   } while (0)
如果您执行以下操作,则此操作将失败:

   int first = 9;
   call_foo(first=first, third=5, second=3);
因为在宏扩展中,
first=first
将意味着使用自身初始化本地
first
。我曾想过尝试使用预处理器串联来解决这个问题,但这会变得更复杂。您必须列出与函数参数相同数量的宏参数

#define call_foo(x, y , z) \
   do { \
      int call_foo_first; \
      int call_foo_second; \
      int call_foo_third;  \
      call_foo_##x;   \
      call_foo_##y;   \
      call_foo_##z;   \
      foo(call_foo_first, call_foo_second, call_foo_third); \
   } while (0)
如果在上一个示例中调用,
call\u foo\u35;#x;
行将变成
call\u foo\u first=first;
,因此每个符号都有自己的唯一名称。 这使得默认参数技巧更加尴尬,因为您必须指定一些东西来填充宏参数列表中的该点

如果宏首先是用
的默认参数定义的,则:

call_foo(first, third=7, second=8);
这将迫使您列出所有参数(或者至少列出一些导致合法C的参数——您可以列出第二个参数两次,但无论如何都可以这样做),以便使用此宏


我认为您应该能够为变量参数列出的宏/函数扩展此函数的最后一个版本,但可选参数仍必须在列表末尾传递,并且您必须在使用可选参数之前用完所有必需的点

“两种语言都支持函数中尾随参数的默认参数。”->否,C没有默认参数。C不允许默认参数。谁在没有明显原因的情况下否决了绝对有效的答案?+1。这种使用params结构的方法称为“命名参数习惯用法”在C++中,我喜欢这个。非常有创意和干净。注意,用这种方法不可能区分零/空参数和省略的参数。为什么要在Booost上添加一个依赖项:任何一个,都要引入编译器优化和性能防火墙,以及硬编码字符串,从而产生潜在的调试噩梦。
#include <iostream>
#include <map>
#include <string>

#include "boost/any.hpp"

using namespace std;

void variableArguments(map<string, boost::any> arguments){
    cout << ">>> variableArguments begins" << endl;

    if(arguments.find("intParameter") != arguments.end()){
    cout << "Found int parameter: " 
         << boost::any_cast<int>(arguments["intParameter"]) << endl;
    }

    if(arguments.find("stringParameter") != arguments.end()){
    cout << "Found string parameter: " 
         << boost::any_cast<string>(arguments["stringParameter"]) << endl;
    }

    cout << "<<< variableArguments ends" << endl;

}

int main(int argc, char *argv[])
{

    map<string, boost::any> argMap;
    argMap["intParameter"] = 5;
    argMap["stringParameter"] = string("MyString");

    variableArguments(argMap);

    argMap.erase(argMap.find("intParameter"));

    variableArguments(argMap);

    return 0;
}