Подтвердить что ты не робот

Избегая скуки факультативных параметров

Если у меня есть конструктор с 2 обязательными параметрами и 4 необязательными параметрами, как я могу избежать написания 16 конструкторов или даже 10 или около того конструкторов, которые мне пришлось бы писать, если бы я использовал параметры по умолчанию (которые мне не нравятся потому что это плохая самодокументация)? Существуют ли какие-либо идиомы или методы, использующие шаблоны, которые я могу использовать, чтобы сделать его менее утомительным? (И проще в обслуживании?)

4b9b3361

Ответ 1

Вам может быть интересен Именованный идентификатор параметров.

Подводя итог, создайте класс, который содержит значения, которые вы хотите передать в свой конструктор (ы). Добавьте метод для установки каждого из этих значений, и каждый из них сделает 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');

Ответ 2

Что делать, если вы создали объект параметра, содержащий все поля? Тогда вы можете просто передать это и просто установить любые поля, в которых вы нуждаетесь. Возможно, это имя для этого шаблона, но не уверен, что это такое...

UPDATE:

Код может выглядеть примерно так:

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.

Ответ 3

И теперь для "Boost есть что-то для этого" ответ:

Boost Parameter Library, кажется, хорошо подходит для вашего использования.

Ответ 4

Все новое для С++ 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);
    }