avoiding the tedium of optional parameters

后端 未结 4 1409
不知归路
不知归路 2020-11-29 06:51

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 writ

相关标签:
4条回答
  • 2020-11-29 07:13

    All new for 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);
        }
    
    0 讨论(0)
  • 2020-11-29 07:32

    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).

    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.

    0 讨论(0)
  • 2020-11-29 07:35

    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:

    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');
    
    0 讨论(0)
  • 2020-11-29 07:39

    And now for the "Boost has something for it" answer:

    The Boost Parameter Library seems to be a good fit to your use case.

    0 讨论(0)
提交回复
热议问题