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?)
如果我有一个带有2个必需参数和4个可选参数的构造函数,如果我使用默认参数(我不喜欢因为它很差),我怎么能避免编写16个构造函数,甚至是我必须编写的10个左右的构造函数自文档)?有没有使用模板的习语或方法,我可以使用它来减少繁琐? (更容易维护?)
4 个解决方案
#1
34
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:
总而言之,创建一个包含要传递给构造函数的值的类。添加一个方法来设置每个值,并让每个方法返回* this;在末尾。在类中有一个构造函数,它接受对这个新类的const引用。这可以这样使用:
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');
#2
9
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).
在someObject构造函数中,您可以为尚未设置的事物设置默认值(如果是必需的话,则引发错误)。
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.
老实说,我不是这个解决方案的忠实粉丝,但是当paramObj通过包含一组通常全部聚集在一起的数据(因此我们可以将其用于更多只是构造函数)时,我已经使用过一次或两次,它比多个构造函数更好。 YMMV,我发现它很丑,但它很有用。
#3
4
And now for the "Boost has something for it" answer:
现在,对于“Boost有所帮助”的答案:
The Boost Parameter Library seems to be a good fit to your use case.
Boost参数库似乎非常适合您的用例。
#4
2
All new for C++17
全新的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);
}
#1
34
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:
总而言之,创建一个包含要传递给构造函数的值的类。添加一个方法来设置每个值,并让每个方法返回* this;在末尾。在类中有一个构造函数,它接受对这个新类的const引用。这可以这样使用:
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');
#2
9
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).
在someObject构造函数中,您可以为尚未设置的事物设置默认值(如果是必需的话,则引发错误)。
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.
老实说,我不是这个解决方案的忠实粉丝,但是当paramObj通过包含一组通常全部聚集在一起的数据(因此我们可以将其用于更多只是构造函数)时,我已经使用过一次或两次,它比多个构造函数更好。 YMMV,我发现它很丑,但它很有用。
#3
4
And now for the "Boost has something for it" answer:
现在,对于“Boost有所帮助”的答案:
The Boost Parameter Library seems to be a good fit to your use case.
Boost参数库似乎非常适合您的用例。
#4
2
All new for C++17
全新的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);
}