繁体   English   中英

调用时指定默认参数 C++ function

[英]Specifying default parameter when calling C++ function

假设我有这样的代码:

void f(int a = 0, int b = 0, int c = 0)
{
    //...Some Code...
}

正如您在上面的代码中明显看到的那样,参数abc的默认参数值为 0。现在看看我下面的主要 function :

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.
}

现在我知道我不能只跳过一个参数,让它具有默认值,因为该值将评估为 position 的参数。 我的意思是,我不能说调用f(a,c) ,因为c将被评估为b ,这是我不想要的,特别是如果c是错误的类型。 Is there a way for the calling function to specify in C++, to use whatever default parameter value there is for the function in any given position, without being limited to going backwards from the last parameter to none? 是否有任何保留关键字来实现这一点,或者至少是一种解决方法? 我可以举一个例子:

f(a, def, c) //Where def would mean default.

没有为此保留字,并且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

请注意,如果不提供 x 和 y 的值,就不可能为 z 提供用户定义的值。 这是因为 C++ 不支持函数调用语法,例如 printValues(,,3)。 这有两个主要后果:

1) 所有默认参数必须是最右边的参数。 不允许出现以下情况:

 void printValue(int x=10, int y); // not allowed

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

不完全是您所要求的,但您可以使用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 中可用。

如果函数的所有参数都是不同类型的,您可以找出哪些参数被传递,哪些没有,并为后者选择默认值。

为了实现不同的类型要求,您可以包装参数并将其传递给可变参数函数模板。 那么即使是参数的顺序也不再重要:

#include <tuple>
#include <iostream>
#include <type_traits>

// -----
// from http://stackoverflow.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 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

活生生的例子

您已经有一个可接受的答案,但这是另一种解决方法(我相信它比其他建议的解决方法具有优势):

您可以强类型参数:

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 的值为负,则抛出”)。
  • 它不会妨碍(适用于默认构造,适用于智能感知/自动完成/与任何其他类一样好)
  • 它是自我记录的。
  • 它和原生版本一样快。

缺点:

  • 增加名称污染(最好将所有这些都放在一个名称空间中)。
  • 虽然简单,但仍然需要维护更多的代码(而不是直接定义函数)。
  • 它可能会引起一些关注(考虑添加关于为什么需要强类型的评论)

我将只使用 static 函数来定义可以更改的默认值:

class defValsExample 
{
public: 
    defValsExample() {
    }

    static int f1def_a() { return 1; }
    static int f1def_b() { return 2; }

    int f1(int a = f1def_a(), int b = f1def_b()) {
        return a+b;
    }
};

int main()
{
    defValsExample t; 

    int c = t.f1(t.f1def_a(),4);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM