调用 C++ 函数时指定默认参数
Posted
技术标签:
【中文标题】调用 C++ 函数时指定默认参数【英文标题】:Specifying default parameter when calling C++ function 【发布时间】:2016-12-12 22:30:47 【问题描述】:假设我有这样的代码:
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++ 中指定,在任何给定位置使用函数的任何默认参数值,而不限于从最后一个参数向后退到无?是否有任何保留关键字来实现这一点,或者至少是一种解决方法?我可以举一个例子:
f(a, def, c) //Where def would mean default.
【问题讨论】:
您可以查看命名参数。有几个技巧可以在 C++ 中将此功能设置为 BOOST_PARAMETER_FUNCTION,然后指定您提供的参数。 如果您似乎需要这样做,您可能存在设计缺陷。我建议你重新评估一下。 @RobK 这只是一个好奇的问题。 【参考方案1】:没有保留字,f(a,,c)
也无效。正如您所展示的,您可以省略一些最右边的可选参数,但不能像这样省略中间的参数。
http://www.learncpp.com/cpp-tutorial/77-default-parameters/
直接引用上面的链接:
多个默认参数
一个函数可以有多个默认参数:
void printValues(int x=10, int y=20, int z=30) std::cout << "Values: " << x << " " << y << " " << z << '\n';
给定以下函数调用:
printValues(1, 2, 3); printValues(1, 2); printValues(1); printValues();
产生以下输出:
Values: 1 2 3 Values: 1 2 30 Values: 1 20 30 Values: 10 20 30
请注意,不可能为 z 提供用户定义的值 不提供 x 和 y 的值。这是因为 C++ 确实 不支持函数调用语法,例如 printValues(,,3)。这有 两大后果:
1) 所有默认参数必须是最右边的参数。这 以下是不允许的:
void printValue(int x=10, int y); // not allowed
2) 如果存在多个默认参数,则最左边的默认参数 参数应该是最有可能由 用户。
【讨论】:
我已经阅读了您的链接,但我已经知道您现在所说的内容。不过感谢您的意见,如果没有更好的答案,我会将其标记为正确。 好的。如果你相信它所说的,那么你的问题就得到了回答。您需要构建您的功能以启用您想要的行为。例如,如果用户输入了一些无效值,可能是 -1,则让它使用默认值。【参考方案2】:作为解决方法,您可以 (ab) 使用 boost::optional
(直到 c++17 中的 std::optional
):
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);
【讨论】:
这是一个很好的最优解。我实际上喜欢这个并且可能会使用它。 +1【参考方案3】:不完全符合您的要求,但您可以使用 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。
【讨论】:
【参考方案4】:如果函数的所有参数都是distinct类型,你可以找出哪些参数被传递,哪些没有,并为后者选择默认值。
为了实现不同的类型要求,您可以包装参数并将其传递给可变参数函数模板。 那么就连参数的顺序也不再重要了:
#include <tuple>
#include <iostream>
#include <type_traits>
// -----
// from http://***.com/a/25958302/678093
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 a10;
B b100;
C c1000;
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
live example
【讨论】:
感谢您的回答,但可变参数模板......无论如何,+1,感谢您的链接 @ArnavBorborah 可变参数模板有什么问题? 省略号不安全吗? @ArnavBorborah 以哪种方式不安全?顺便说一句:可变参数模板不与 C 省略号相同 哦,好吧,我把他们弄糊涂了,另一个堆栈溢出答案,提到 c - 省略号很危险。【参考方案5】:您已经有一个公认的答案,但这是另一种解决方法(我相信它比其他建议的解决方法具有优势):
您可以对参数进行强类型化:
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, C2);
f(, , 3);
优点:
它简单且易于维护(每个参数一行)。 为进一步限制 API 提供了一个自然点(例如,“如果 B 的值为负,则抛出”)。 它不会妨碍(使用默认构造,使用智能感知/自动完成/与任何其他类一样好) 它是自我记录的。 与原生版本一样快。缺点:
增加名称污染(最好将所有这些都放在命名空间中)。 虽然简单,但仍然需要维护更多代码(而不仅仅是直接定义函数)。 它可能会引起一些人的注意(考虑添加关于为什么需要强类型的评论)【讨论】:
以上是关于调用 C++ 函数时指定默认参数的主要内容,如果未能解决你的问题,请参考以下文章