Избегая скуки необязательных параметров
Если у меня есть конструктор с, скажем, 2 обязательными параметрами и 4 необязательными параметрами, как я могу избежать написания 16 конструкторов или даже 10 или около того конструкторов, которые я должен был бы написать, если бы использовал параметры по умолчанию (что мне не нравится, потому что это плохо самодокументирование)? Существуют ли идиомы или методы, использующие шаблоны, которые я могу использовать, чтобы сделать его менее утомительным? (А проще в обслуживании?)
4 ответа
Возможно, вас заинтересует идиома именованных параметров.
Подводя итог, создайте класс, который содержит значения, которые вы хотите передать своим конструкторам. Добавьте метод для установки каждого из этих значений, и пусть каждый метод выполняет 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');
Что если вы сделали объект параметра, который содержал все поля? Тогда вы можете просто пропустить это и установить те поля, которые вам нужны. Вероятно, есть название для этой модели, но я не уверен, что это такое...
ОБНОВИТЬ:
Код может выглядеть примерно так:
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 есть что-то для этого" ответ:
Похоже, библиотека Boost Parameter подходит для вашего случая использования.
Все новое для 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;
}
У меня есть реализация кубического сплайна, которая позволяет пользователю опционально указать первую производную на левом конце, на правом конце или на обоих. Если производная не указана, то действующий код вычисляет единицу, предполагая, что вторая производная равна нулю (так называемый "естественный сплайн"). Вот фрагмент для левого конца.
// 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);
}