关于C++:避免可选参数单调乏味

avoiding the tedium of optional parameters

如果我有一个构造器有2个必需参数和4个可选参数,我怎么能避免写16个构造器,甚至10个左右的构造器,如果我使用默认参数(我不喜欢,因为这是糟糕的自我文档)?有没有使用模板的成语或方法可以让它不那么乏味?(更容易维护?)


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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');


如果您创建了一个包含所有字段的参数对象怎么办?然后你可以通过它,设置你需要的任何字段。这个模式可能有个名字,但不确定它是什么…

更新:

代码可能看起来有点像这样:

1
2
3
4
5
6
7
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。


现在,对于"提振有为"的回答:

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


C++的所有新17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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
2
3
4
5
6
7
8
// 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);
    }