如何清楚地指定我传递的参数和哪些参数保持默认值?

时间:2011-11-18 06:03:00

标签: c++ c++11 overloading default-arguments

因为这个问题Default argument in c++

说我有这样的功能:void f(int p1=1, int p2=2, int p3=3, int p4=4);

我想仅使用一些参数调用它 - 其余的将是默认值。

这样的事情会起作用:

template<bool P1=true, bool P2=true, bool P3=true, bool P4=true>
void f(int p1=1, int p2=2, int p3=3, int p4=4);
// specialize:
template<>
void f<false, true, false, false>(int p1) {
  f(1, p1);
}
template<>
void f<false, true, true, false>(int p1, int p2) {
  f(1, p1, p2);
}
// ... and so on. 
// Would need a specialization for each combination of arguments
// which is very tedious and error-prone

// Use:
f<false, true, false, false>(5); // passes 5 as p2 argument

但它需要太多的代码才能实用。

有更好的方法吗?

4 个答案:

答案 0 :(得分:11)

使用命名参数成语(→FAQ link)。

Boost.Parameters库(→link)也可以解决此任务,但代码冗长并大大降低了清晰度。它在处理构造函数方面也存在缺陷。当然,它需要安装Boost库。

干杯&amp;第h。,

答案 1 :(得分:7)

查看Boost.Parameter库。

它在C ++中实现命名参数。例如:

#include <boost/parameter/name.hpp>
#include <boost/parameter/preprocessor.hpp>
#include <iostream>

//Define
BOOST_PARAMETER_NAME(p1)    
BOOST_PARAMETER_NAME(p2)
BOOST_PARAMETER_NAME(p3)
BOOST_PARAMETER_NAME(p4)

BOOST_PARAMETER_FUNCTION(
                         (void),
                         f,
                         tag,
                         (optional            
                          (p1, *, 1)
                          (p2, *, 2)
                          (p3, *, 3)
                          (p4, *, 4)))
{
    std::cout << "p1: " << p1 
            << ", p2: " << p2
            << ", p3: " << p3
            << ", p4: " << p4 << "\n";
}
//Use
int main()
{
    //Prints "p1: 1, p2: 5, p3: 3, p4: 4"
    f(_p2=5);
}

答案 2 :(得分:4)

尽管Boost.Parameters很有趣,但它(很遗憾)会遇到许多问题,其中包括占位符冲突(并且必须调试古怪的预处理器/模板错误):

BOOST_PARAMETER_NAME(p1)

将创建您稍后使用的_p1占位符。如果您有两个不同的标头声明相同的占位符,则会出现冲突。不好玩。

基于Builder模式,有一个更为简单(概念上和实际上)的答案,有些是命名参数成语

而不是指定这样的函数:

void f(int a, int b, int c = 10, int d = 20);

您指定的结构将覆盖operator()

  • 构造函数用于询问强制参数(不是严格地在命名参数成语中,但是没有人说你必须盲目地遵循它),并且为可选参数设置了默认值
  • 每个可选参数都有一个setter

通常,它与 Chaining 结合使用,其中包括使setter返回对当前对象的引用,以便可以将调用链接在一行上。

class f {
public:
  // Take mandatory arguments, set default values
  f(int a, int b): _a(a), _b(b), _c(10), _d(20) {}

  // Define setters for optional arguments
  // Remember the Chaining idiom
  f& c(int v) { _c = v; return *this; }
  f& d(int v) { _d = v; return *this; }

  // Finally define the invocation function
  void operator()() const;

private:
  int _a;
  int _b;
  int _c;
  int _d;
}; // class f

调用是:

f(/*a=*/1, /*b=*/2).c(3)(); // the last () being to actually invoke the function

我已经看到一个变量将强制参数作为参数添加到operator(),这避免了将参数保留为属性,但语法有点怪异:

f().c(3)(/*a=*/1, /*b=*/2);

一旦编译器内联了所有构造函数和setter调用(这就是为什么它们在这里被定义,而operator()不是),与“常规”函数调用相比,它应该产生类似的高效代码。 / p>

答案 3 :(得分:2)

这不是一个真正的答案,但是......

David Abrahams和Aleksey Gurtovoy撰写的C++ Template Metaprogramming(2004年出版!)作者谈到了这一点:

  

在编写本书时,我们重新考虑了用于命名的界面   功能参数支持。通过一些实验我们   发现可以通过使用提供理想的语法   具有重载赋值运算符的关键字对象:

f(slew = .799, name = "z");

他们接着说:

  

我们不打算深入了解这个命名的实现细节   参数库在这里;我们建议,这很简单   你自己尝试将它作为一种练习来实现。

这是在模板元编程和Boost :: MPL的上下文中。我不太确定他们的“直截了​​当”实现如何使用默认参数,但我认为它是透明的。