调用C++函数时指定默认参数
Specifying default parameter when calling C++ function
假设我有这样的代码:
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
正如您在上面的代码中明显看到的那样,参数 a
、b
和 c
的默认参数值为 0。现在看看我的主函数以下:
int main()
{
//Here are 4 ways of calling the above function:
int a = 2;
int b = 3;
int c = -1;
f(a, b, c);
f(a, b);
f(a);
f();
//note the above parameters could be changed for the other variables
//as well.
}
现在我知道我不能只跳过一个参数,让它具有默认值,因为该值将作为该位置的参数求值。我的意思是,我不能说 f(a,c)
,因为 c
会被评估为 b
,这是我不想要的,尤其是如果 c
] 是错误的类型。有没有一种方法可以让调用函数在 C++ 中指定,在任何给定位置使用函数的任何默认参数值,而不限于从最后一个参数向后返回 none?是否有任何保留关键字可以实现此目的,或者至少有一个解决方法?我可以举一个例子:
f(a, def, c) //Where def would mean default.
这里没有保留字,f(a,,c)
也无效。您可以省略一些最右边的可选参数,如您所示,但不能像那样省略中间的参数。
http://www.learncpp.com/cpp-tutorial/77-default-parameters/
直接引用上面的link:
Multiple default parameters
A function can have multiple default parameters:
void printValues(int x=10, int y=20, int z=30)
{
std::cout << "Values: " << x << " " << y << " " << z << '\n';
}
Given the following function calls:
printValues(1, 2, 3);
printValues(1, 2);
printValues(1);
printValues();
The following output is produced:
Values: 1 2 3
Values: 1 2 30
Values: 1 20 30
Values: 10 20 30
Note that it is impossible to supply a user-defined value for z
without also supplying a value for x and y. This is because C++ does
not support a function call syntax such as printValues(,,3). This has
two major consequences:
1) All default parameters must be the rightmost parameters. The
following is not allowed:
void printValue(int x=10, int y); // not allowed
2) If more than one default parameter exists, the leftmost default
parameter should be the one most likely to be explicitly set by the
user.
不完全符合您的要求,但您可以使用 std::bind()
来固定参数的值。
类似
#include <functional>
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
int main()
{
// Here are 4 ways of calling the above function:
int a = 2;
int b = 3;
int c = -1;
f(a, b, c);
f(a, b);
f(a);
f();
// note the above parameters could be changed
// for the other variables as well.
using namespace std::placeholders; // for _1, _2
auto f1 = std::bind(f, _1, 0, _2);
f1(a, c); // call f(a, 0, c);
return 0;
}
使用 std::bind()
您可以修复与默认参数值不同的值或没有默认值的参数值。
请注意 std::bind()
仅适用于 C++11。
如果函数的所有参数都是distinct类型,你可以找出哪些参数被传递了,哪些没有传递,后者选择默认值。
为了达到不同的类型要求,您可以包装您的参数并将其传递给可变函数模板。
那么即使参数的顺序也不再重要了:
#include <tuple>
#include <iostream>
#include <type_traits>
// -----
// from
template <typename T, typename Tuple>
struct has_type;
template <typename T>
struct has_type<T, std::tuple<>> : std::false_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
template <typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;
//------
template <typename Tag, typename T, T def>
struct Value{
Value() : v(def){}
Value(T v) : v(v){}
T v;
};
using A = Value<struct A_, int, 1>;
using B = Value<struct B_, int, 2>;
using C = Value<struct C_, int, 3>;
template <typename T, typename Tuple>
std::enable_if_t<tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple t)
{
return std::get<T>(t);
}
template <typename T, typename Tuple>
std::enable_if_t<!tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple)
{
return T{};
}
template <typename InputTuple, typename... Params>
auto getValueOrDefault(std::tuple<Params...>, InputTuple t)
{
return std::make_tuple(getValueOrDefaultImpl<Params>(t)...);
}
template <typename... Params, typename ArgTuple>
auto getParams(ArgTuple argTuple)
{
using ParamTuple = std::tuple<Params...>;
ParamTuple allValues = getValueOrDefault(ParamTuple{}, argTuple);
return allValues;
}
template <typename... Args>
void f(Args ... args)
{
auto allParams = getParams<A,B,C>(std::make_tuple(args...));
std::cout << "a = " << std::get<A>(allParams).v << " b = " << std::get<B>(allParams).v << " c = " << std::get<C>(allParams).v << std::endl;
}
int main()
{
A a{10};
B b{100};
C c{1000};
f(a, b, c);
f(b, c, a);
f(a, b);
f(a);
f();
}
输出
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 3
a = 10 b = 2 c = 3
a = 1 b = 2 c = 3
作为解决方法,您可以(滥用)使用 boost::optional
(直到 std::optional
来自 c++17):
void f(boost::optional<int> oa = boost::none,
boost::optional<int> ob = boost::none,
boost::optional<int> oc = boost::none)
{
int a = oa.value_or(0); // Real default value go here
int b = ob.value_or(0); // Real default value go here
int c = oc.value_or(0); // Real default value go here
//...Some Code...
}
然后调用它
f(a, boost::none, c);
您已经有了一个可接受的答案,但这里有另一个解决方法(我相信它比其他提议的解决方法有优势):
您可以对参数进行强类型化:
struct A { int value = 0; };
struct B { int value = 2; };
struct C { int value = 4; };
void f(A a = {}, B b = {}, C c = {}) {}
void f(A a, C c) {}
int main()
{
auto a = 0;
auto b = -5;
auto c = 1;
f(a, b, c);
f(a, C{2});
f({}, {}, 3);
}
优点:
- 它简单易维护(每个参数一行)。
- 为进一步压缩 API 提供了一个自然点(例如,“如果 B 的值为负数则抛出”)。
- 它不会妨碍(与默认结构一起工作,与 intellisense/auto-complete/whatever 一起工作与任何其他 class 一样好)
- 它是自我记录的。
- 与原生版本一样快。
缺点:
- 增加名称污染(最好将所有这些都放在名称空间中)。
- 虽然简单,但仍然需要维护更多代码(而不是直接定义函数)。
- 它可能会引起一些人的注意(考虑添加关于为什么需要强类型的评论)
假设我有这样的代码:
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
正如您在上面的代码中明显看到的那样,参数 a
、b
和 c
的默认参数值为 0。现在看看我的主函数以下:
int main()
{
//Here are 4 ways of calling the above function:
int a = 2;
int b = 3;
int c = -1;
f(a, b, c);
f(a, b);
f(a);
f();
//note the above parameters could be changed for the other variables
//as well.
}
现在我知道我不能只跳过一个参数,让它具有默认值,因为该值将作为该位置的参数求值。我的意思是,我不能说 f(a,c)
,因为 c
会被评估为 b
,这是我不想要的,尤其是如果 c
] 是错误的类型。有没有一种方法可以让调用函数在 C++ 中指定,在任何给定位置使用函数的任何默认参数值,而不限于从最后一个参数向后返回 none?是否有任何保留关键字可以实现此目的,或者至少有一个解决方法?我可以举一个例子:
f(a, def, c) //Where def would mean default.
这里没有保留字,f(a,,c)
也无效。您可以省略一些最右边的可选参数,如您所示,但不能像那样省略中间的参数。
http://www.learncpp.com/cpp-tutorial/77-default-parameters/
直接引用上面的link:
Multiple default parameters
A function can have multiple default parameters:
void printValues(int x=10, int y=20, int z=30) { std::cout << "Values: " << x << " " << y << " " << z << '\n'; }
Given the following function calls:
printValues(1, 2, 3); printValues(1, 2); printValues(1); printValues();
The following output is produced:
Values: 1 2 3 Values: 1 2 30 Values: 1 20 30 Values: 10 20 30
Note that it is impossible to supply a user-defined value for z without also supplying a value for x and y. This is because C++ does not support a function call syntax such as printValues(,,3). This has two major consequences:
1) All default parameters must be the rightmost parameters. The following is not allowed:
void printValue(int x=10, int y); // not allowed
2) If more than one default parameter exists, the leftmost default parameter should be the one most likely to be explicitly set by the user.
不完全符合您的要求,但您可以使用 std::bind()
来固定参数的值。
类似
#include <functional>
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
int main()
{
// Here are 4 ways of calling the above function:
int a = 2;
int b = 3;
int c = -1;
f(a, b, c);
f(a, b);
f(a);
f();
// note the above parameters could be changed
// for the other variables as well.
using namespace std::placeholders; // for _1, _2
auto f1 = std::bind(f, _1, 0, _2);
f1(a, c); // call f(a, 0, c);
return 0;
}
使用 std::bind()
您可以修复与默认参数值不同的值或没有默认值的参数值。
请注意 std::bind()
仅适用于 C++11。
如果函数的所有参数都是distinct类型,你可以找出哪些参数被传递了,哪些没有传递,后者选择默认值。
为了达到不同的类型要求,您可以包装您的参数并将其传递给可变函数模板。 那么即使参数的顺序也不再重要了:
#include <tuple>
#include <iostream>
#include <type_traits>
// -----
// from
template <typename T, typename Tuple>
struct has_type;
template <typename T>
struct has_type<T, std::tuple<>> : std::false_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
template <typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;
//------
template <typename Tag, typename T, T def>
struct Value{
Value() : v(def){}
Value(T v) : v(v){}
T v;
};
using A = Value<struct A_, int, 1>;
using B = Value<struct B_, int, 2>;
using C = Value<struct C_, int, 3>;
template <typename T, typename Tuple>
std::enable_if_t<tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple t)
{
return std::get<T>(t);
}
template <typename T, typename Tuple>
std::enable_if_t<!tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple)
{
return T{};
}
template <typename InputTuple, typename... Params>
auto getValueOrDefault(std::tuple<Params...>, InputTuple t)
{
return std::make_tuple(getValueOrDefaultImpl<Params>(t)...);
}
template <typename... Params, typename ArgTuple>
auto getParams(ArgTuple argTuple)
{
using ParamTuple = std::tuple<Params...>;
ParamTuple allValues = getValueOrDefault(ParamTuple{}, argTuple);
return allValues;
}
template <typename... Args>
void f(Args ... args)
{
auto allParams = getParams<A,B,C>(std::make_tuple(args...));
std::cout << "a = " << std::get<A>(allParams).v << " b = " << std::get<B>(allParams).v << " c = " << std::get<C>(allParams).v << std::endl;
}
int main()
{
A a{10};
B b{100};
C c{1000};
f(a, b, c);
f(b, c, a);
f(a, b);
f(a);
f();
}
输出
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 3
a = 10 b = 2 c = 3
a = 1 b = 2 c = 3
作为解决方法,您可以(滥用)使用 boost::optional
(直到 std::optional
来自 c++17):
void f(boost::optional<int> oa = boost::none,
boost::optional<int> ob = boost::none,
boost::optional<int> oc = boost::none)
{
int a = oa.value_or(0); // Real default value go here
int b = ob.value_or(0); // Real default value go here
int c = oc.value_or(0); // Real default value go here
//...Some Code...
}
然后调用它
f(a, boost::none, c);
您已经有了一个可接受的答案,但这里有另一个解决方法(我相信它比其他提议的解决方法有优势):
您可以对参数进行强类型化:
struct A { int value = 0; };
struct B { int value = 2; };
struct C { int value = 4; };
void f(A a = {}, B b = {}, C c = {}) {}
void f(A a, C c) {}
int main()
{
auto a = 0;
auto b = -5;
auto c = 1;
f(a, b, c);
f(a, C{2});
f({}, {}, 3);
}
优点:
- 它简单易维护(每个参数一行)。
- 为进一步压缩 API 提供了一个自然点(例如,“如果 B 的值为负数则抛出”)。
- 它不会妨碍(与默认结构一起工作,与 intellisense/auto-complete/whatever 一起工作与任何其他 class 一样好)
- 它是自我记录的。
- 与原生版本一样快。
缺点:
- 增加名称污染(最好将所有这些都放在名称空间中)。
- 虽然简单,但仍然需要维护更多代码(而不是直接定义函数)。
- 它可能会引起一些人的注意(考虑添加关于为什么需要强类型的评论)