如何清楚地指定我正在传递哪些参数以及哪些保持默认?

发布于 2024-12-16 16:49:21 字数 818 浏览 0 评论 0原文

因此询问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

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

有更好的方法吗?

Asked because of this: Default argument in c++

Say I have a function such as this: void f(int p1=1, int p2=2, int p3=3, int p4=4);

And I want to call it using only some of the arguments - the rest will be the defaults.

Something like this would work:

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

But it requires too much code to be practical.

Is there a better way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

谈下烟灰 2024-12-23 16:49:21

使用命名参数习惯用法(→ 常见问题解答链接)。

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

Use the Named Parameters Idiom (→ FAQ link).

The Boost.Parameters library (→ link) can also solve this task, but paid for by code verbosity and greatly reduced clarity. It's also deficient in handling constructors. And it requires having the Boost library installed, of course.

恏ㄋ傷疤忘ㄋ疼 2024-12-23 16:49:21

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

Have a look at the Boost.Parameter library.

It implements named paramaters in C++. Example:

#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);
}
尐籹人 2024-12-23 16:49:21

尽管 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() 不是),与“常规”函数调用相比,它应该会产生类似的高效代码。

Although Boost.Parameters is amusing, it suffers (unfortunately) for a number of issues, among which placeholder collision (and having to debug quirky preprocessors/template errors):

BOOST_PARAMETER_NAME(p1)

Will create the _p1 placeholder that you then use later on. If you have two different headers declaring the same placeholder, you get a conflict. Not fun.

There is a much simpler (both conceptually and practically) answer, based on the Builder Pattern somewhat is the Named Parameters Idiom.

Instead of specifying such a function:

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

You specify a structure, on which you will override the operator():

  • the constructor is used to ask for mandatory arguments (not strictly in the Named Parameters Idiom, but nobody said you had to follow it blindly), and default values are set for the optional ones
  • each optional parameter is given a setter

Generally, it is combined with Chaining which consists in making the setters return a reference to the current object so that the calls can be chained on a single line.

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

The invocation is:

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

I've seen a variant putting the mandatory arguments as parameters to operator(), this avoids keeping the arguments as attributes but the syntax is a bit weirder:

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

Once the compiler has inlined all the constructor and setters call (which is why they are defined here, while operator() is not), it should result in similarly efficient code compared to the "regular" function invocation.

野稚 2024-12-23 16:49:21

这并不是真正的答案,但是......

C++模板元编程 David Abrahams 和 Aleksey Gurtovoy(2004 年出版!)作者对此进行了讨论:

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

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

他们接着说:

我们不会深入讨论这个命名的实现细节
参数库在这里;这很简单,我们建议
您尝试自己实现它作为练习。

这是在模板元编程和 Boost::MPL 的背景下。我不太确定他们的“直接”实现如何与默认参数配合,但我认为它是透明的。

This isn't really an answer, but...

In C++ Template Metaprogramming by David Abrahams and Aleksey Gurtovoy (published in 2004!) the authors talk about this:

While writing this book, we reconsidered the interface used for named
function parameter support. With a little experimentation we
discovered that it’s possible to provide the ideal syntax by using
keyword objects with overloaded assignment operators:

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

They go on to say:

We’re not going to get into the implementation details of this named
parameter library here; it’s straightforward enough that we suggest
you try implementing it yourself as an exercise.

This was in the context of template metaprogramming and Boost::MPL. I'm not too sure how their "straighforward" implementation would jive with default parameters, but I assume it would be transparent.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文