默认参数:只能留下最后一个参数吗?

21

我知道可以像这样做:

int foo(int a = 0, int b = 1) { return a + b; }

然后在不使用默认参数的情况下使用它,例如:

foo();    // a = 0, b = 1 -> 1

或者默认使用最后一个,例如:

foo(2);    // a = 2 and b = 1 default -> 3

但是我的问题是:是否可以使用第一个参数(a)的默认值,并给出第二个参数(b)的值?

我最初的想法是这样做(但不起作用!):

foo(,2);    // a = 0 default and b = 2

是否存在适用于此的语法,还是这不可能实现?


2
不行,这是做不到的。 - Justin Time - Reinstate Monica
1
密切相关;http://stackoverflow.com/questions/39818748/what-should-the-default-argumentif-any-be-while-passing-a-vector-to-a-function - πάντα ῥεῖ
1
不可能的,缺省参数必须放在最右边。 - Raindrop7
7个回答

21

不,目前的语法中是不可能实现的。


8

除了指定默认参数值之外,您还可以使用多个函数重载,例如:

 int foo(int a, int b){return a+b; }
 int foo(int b){return foo(0,b); }     
 int foo(){return foo(0,1); }

这不会产生与 foo(int b=1, int a=0){..} 相同的效果吗? - UNKN57
@UNKN57 嗯,是的。但是与 int foo(int a=0, int b=1) 有不同的效果。而且你不能同时声明两个,因为会产生歧义。 - πάντα ῥεῖ
@UNKN57 也许你的例子过于简化了。对于我的建议来说,关于消除参数类型歧义,还有更多的变化可能性。 - πάντα ῥεῖ
对于剩余的参数,可以在重载中使用特殊类型,例如https://en.cppreference.com/w/cpp/utility/optional/nullopt_t。然后所有组合都是可能的。当然,这些参数本身可以在第一次使用时就是`std::optional`; 那么就不需要重载了。 - Sebastian
为什么不使用类似 int foo({.b=2}); 的东西,其中 foo 接受一个使用聚合初始化的聚合体:https://en.cppreference.com/w/cpp/language/aggregate_initialization - Fabian Keßler
显示剩余4条评论

1

虽然还不能像 OP 期望的那样实现,但自从 以来,可以使用 std::optional 来模拟这种行为。

不要传递 int 作为参数,而是传递默认值为 {}optional<int>

int foo(std::optional<int> in_a = {}, std::optional<int> in_b = {});

如果用户没有提供值(传递了{}std::nullopt),则在函数体中使用“真实”的默认值进行替换:

int foo(std::optional<int> in_a = {}, std::optional<int> in_b = {})
{
    const auto a = in_a.value_or(default_value_for_a);
    const auto b = in_b.value_or(default_value_for_b);

    return a + b;
}

例如,假设您想要默认值为a但不是b,现在可以这样做:
foo({}, value_for_b);

当添加更多参数时,这个通用方法也适用。唯一的限制是你必须能够将类型放在可选中,这方面有一些限制


1

直接做不到。但是,如果您为封装默认值的输入参数声明结构体并具有单独的setter,则可以通过某种方式实现:

#include <iostream>

struct Foo{
    int a,b;
    Foo() : a(0),b(1) {};
    Foo setA(int x) { a=x; return *this;}
    Foo setB(int x) { b=x; return *this;}
};

void foo(Foo f = Foo()){ std::cout << f.a << "  " << f.b << std::endl; }

int main() {
    foo();                // uses default values for a and b
    foo(Foo().setA(3));   // uses default value for b
    foo(Foo().setB(5));   // uses default value for a
}

当然,这会增加一些开销。另一方面,在我看来,将参数封装到结构体中并在那里提供默认值,而不是在函数声明中提供默认值,通常是有意义的。


为什么不将聚合初始化与作为函数参数的聚合结合起来使用?https://en.cppreference.com/w/cpp/language/aggregate_initialization - Fabian Keßler
@FabianKeßler 没有“为什么不”。答案的重点只是使用自定义结构体。 - 463035818_is_not_a_number
聚合体也是一种自定义结构体,但它不需要设置器:https://godbolt.org/z/9556jqard - Fabian Keßler
@FabianKeßler 我知道,如果它适合你就用吧。这个答案是2016年的,当时c++20并不普遍可用。考虑一下这个:https://godbolt.org/z/7hK33dnac - 463035818_is_not_a_number
是的,我知道这是c++20的一个特性。我只是好奇为什么这个问题会出现在我的动态前面。不过,你可以把c++20用户的聚合版本添加到你的答案中。 - Fabian Keßler

1

一旦我们为参数提供了默认值,所有后续的参数也必须有默认值。例如:

// Invalid
void add(int a, int b = 3, int c, int d);

// Invalid
void add(int a, int b = 3, int c, int d = 4);

// Valid
void add(int a, int c, int b = 3, int d = 4);

0
我知道这不是你问题的真正答案,但你可以尝试使用 命名参数 Boost。它可能对你的使用情况有用。

1
我还没有实际用例。在学习时只是自己问了一下(我是一个完全的C ++新手)。但您提供的链接似乎非常有帮助! - UNKN57

-2

不,这是不可能的。 第一个参数必须填入某个值。


这并没有提供比已接受答案更多的信息。 - Sebastian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接