избегая скуки необязательных параметров - PullRequest
16 голосов
/ 23 апреля 2010

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

Ответы [ 4 ]

34 голосов
/ 23 апреля 2010

Возможно, вас заинтересует идиома именованных параметров .

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

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

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.

4 голосов
/ 23 апреля 2010

А теперь для «Boost есть что-то для этого» ответ:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

2 голосов
/ 20 января 2018

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