避免繁琐的可选参数

发布于 2024-08-30 13:23:03 字数 143 浏览 6 评论 0原文

如果我有一个带有 2 个必需参数和 4 个可选参数的构造函数,那么如何避免编写 16 个构造函数,甚至如果使用默认参数(我不喜欢它,因为它很差),我就必须编写 10 个左右的构造函数自我文档)?是否有任何使用模板的惯用语或方法可以用来使其不那么乏味? (并且更容易维护?)

If I have a constructor with say 2 required parameters and 4 optional parameters, how can I avoid writing 16 constructors or even the 10 or so constructors I'd have to write if I used default parameters (which I don't like because it's poor self-documentation)? Are there any idioms or methods using templates I can use to make it less tedious? (And easier to maintain?)

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

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

发布评论

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

评论(4

痴骨ら 2024-09-06 13:23:03

您可能对命名参数惯用语感兴趣。

总而言之,创建一个类来保存要传递给构造函数的值。添加一个方法来设置每个值,并让每个方法在最后执行 return *this; 操作。在你的类中有一个构造函数,它接受这个新类的常量引用。可以像这样使用:

class Person;

class PersonOptions
{
  friend class Person;
  string name_;
  int age_;
  char gender_;

public:
   PersonOptions() :
     age_(0),
     gender_('U')
   {}

   PersonOptions& name(const string& n) { name_ = n; return *this; }
   PersonOptions& age(int a) { age_ = a; return *this; }
   PersonOptions& gender(char g) { gender_ = g; return *this; }
};

class Person
{
  string name_;
  int age_;
  char gender_;

public:
   Person(const PersonOptions& opts) :
     name_(opts.name_),
     age_(opts.age_),
     gender_(opts.gender_)
   {}
};
Person p = PersonOptions().name("George").age(57).gender('M');

You might be interested in the Named Parameter Idiom.

To summarize, create a class that holds the values you want to pass to your constructor(s). Add a method to set each of those values, and have each method do a return *this; at the end. Have a constructor in your class that takes a const reference to this new class. This can be used like so:

class Person;

class PersonOptions
{
  friend class Person;
  string name_;
  int age_;
  char gender_;

public:
   PersonOptions() :
     age_(0),
     gender_('U')
   {}

   PersonOptions& name(const string& n) { name_ = n; return *this; }
   PersonOptions& age(int a) { age_ = a; return *this; }
   PersonOptions& gender(char g) { gender_ = g; return *this; }
};

class Person
{
  string name_;
  int age_;
  char gender_;

public:
   Person(const PersonOptions& opts) :
     name_(opts.name_),
     age_(opts.age_),
     gender_(opts.gender_)
   {}
};
Person p = PersonOptions().name("George").age(57).gender('M');
像极了他 2024-09-06 13:23:03

如果您创建一个包含所有字段的参数对象怎么办?然后你可以直接传递它,并设置你需要的任何字段。该模式可能有一个名称,但不确定它是什么...

更新:

代码可能看起来像这样:

paramObj.x=1;
paramObj.y=2;
paramObj.z=3;
paramObj.magic=true;
... //set many other "parameters here"

someObject myObject = new someObject(paramObj);

someObject 构造函数中,您可以设置默认值尚未设置的内容(如果是强制性的,则会引发错误)。

老实说,我不太喜欢这个解决方案,但是当 paramObj 包含一组通常都在一起的数据有意义时,我已经使用过它一两次(所以我们可以使用它不仅仅是构造函数),而且它比多个构造函数更好。我发现它很丑,但它有效,YMMV。

What if you made a parameter object that contained all the fields? Then you could just pass that, and just set whichever fields you need. There's probably a name for that pattern, not sure what it is though...

UPDATE:

Code might look like somewhat this:

paramObj.x=1;
paramObj.y=2;
paramObj.z=3;
paramObj.magic=true;
... //set many other "parameters here"

someObject myObject = new someObject(paramObj);

and inside the someObject constructor you can set defaults for things that were not already set (or raise an error if it was mandatory).

Honestly, I'm not a big fan of this solution, but I've used it once or twice when paramObj made sense by containing a set of data that usually all went together (so we could use it for more than just constructors), and it was better than multiple constructors. I found that it was ugly but it worked, YMMV.

只为守护你 2024-09-06 13:23:03

现在对于“Boost 有其用处”的答案:

Boost 参数库 似乎很适合您的用例。

And now for the "Boost has something for it" answer:

The Boost Parameter Library seems to be a good fit to your use case.

娜些时光,永不杰束 2024-09-06 13:23:03

对于 C++17 来说,

#include <optional>

using optional_int = std::optional<int>;

class foo {
    int arg0, arg1; // required
    int arg2, arg3; // optional
    const int default_2 = -2;
    const int default_3 = -3;
public:
    foo(int arg0, int arg1, optional_int opt0 = {}, optional_int opt1 = {})
        : arg0(arg0), arg1(arg1)
        , arg2(opt0.value_or(default_2))
        , arg3(opt1.value_or(default_3))
    { }

};

int main() {
    foo bar(42, 43, {}, 45); // Take default for opt0 (arg2)
    return 0;
}

我有一个三次样条实现,它允许用户选择在左端、右端或两者处指定一阶导数。如果未指定导数,则代码实际上会通过假设二阶导数为零(所谓的“自然样条”)来计算 1。这是左端的片段。

// Calculate the second derivative at the left end point
    if (!left_deriv.has_value()) {
        ddy[0]=u[0]=0.0; // "Natural spline"
    } else {
        const real yP0 = left_deriv.value();
        ddy[0] = -0.5;
        u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yP0);
    }

All new for C++17

#include <optional>

using optional_int = std::optional<int>;

class foo {
    int arg0, arg1; // required
    int arg2, arg3; // optional
    const int default_2 = -2;
    const int default_3 = -3;
public:
    foo(int arg0, int arg1, optional_int opt0 = {}, optional_int opt1 = {})
        : arg0(arg0), arg1(arg1)
        , arg2(opt0.value_or(default_2))
        , arg3(opt1.value_or(default_3))
    { }

};

int main() {
    foo bar(42, 43, {}, 45); // Take default for opt0 (arg2)
    return 0;
}

I have a cubic spline implementation that allows the user optionally to specify the first derivative at either the left end, the right end, or both. If a derivative is not specified, then the code in effect calculates one, by assuming that the second derivative is zero (the so-called "natural spline"). Here is a fragment for the left end.

// Calculate the second derivative at the left end point
    if (!left_deriv.has_value()) {
        ddy[0]=u[0]=0.0; // "Natural spline"
    } else {
        const real yP0 = left_deriv.value();
        ddy[0] = -0.5;
        u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yP0);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文