lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Default values for default arguments


From: Vadim Zeitlin
Subject: Re: [lmi] Default values for default arguments
Date: Sat, 11 Feb 2017 17:59:45 +0100

On Sat, 11 Feb 2017 10:44:00 +0000 Greg Chicares <address@hidden> wrote:

GC> On 2017-02-11 10:16, Greg Chicares wrote:
GC> > On 2017-02-10 16:58, Vadim Zeitlin wrote:
GC> >> 
GC> >>  I'd also consider using "Foo{}" instead of "Foo()" for the constructors 
of
GC> >> temporary objects (although lmi code doesn't use them much, I think),
GC> >> because the former is much more clear as it doesn't look like a function
GC> >> call.
GC> > 
GC> > Discussion of selected `grep 'std::string()'` results:
GC> > 
GC> > datum_sequence.cpp:    return std::string();
GC> > rate_table_test.cpp:        stream_out_.str(std::string());
GC> > rate_table_tool.cpp:        name_map.emplace(num, std::string());
GC> > 
GC> > In those cases, the type 'std::string' must be named, and I think you're
GC> > saying you'd prefer {} to ().

 Yes, I prefer it, but just because it seems more readable to me and I
realize that it's not an objective argument.

GC> > antediluvian_stubs.cpp:    std::string const empty_string = std::string();
GC> > 
GC> > How about that one? Which of the following...
GC> >   std::string const empty_string = std::string(); // Leave it alone.
GC> >   std::string const empty_string = std::string{}; // Prefer {} to ().
GC> >   std::string const empty_string{}; // Don't even name the type.

 If it's a variable declaration, then I definitely prefer the last one,
repeating the type here doesn't seem to serve any useful purpose. The
real choice here for me is between

        std::string const empty_string{}; // Don't even name the type.
        std::string const empty_string;   // Shortest possible.

 Here I also (slightly) prefer the version with "{}" and I think I do have
an objective argument in favour of it: consistency with the primitive
types. For std::string the two lines are exactly equivalent, but

        double zero_double{};
        double uninitialized_double;

are not and using "{}" makes things more clear.

GC> > wx_test_paste_census.cpp:    ,std::string const& unexpected = 
std::string()
GC> > 
GC> > How about this one, which is a defaulted argument? I ask this because
GC> > earlier we had decided to write that exactly as above. But it's okay
GC> > to change our minds; which would you now prefer?
GC> >   ,std::string const& unexpected = std::string()
GC> >   ,std::string const& unexpected = std::string{}
GC> >   ,std::string const& unexpected{}

 The last one shouldn't compile AFAICS. If you meant

        ,std::string const& unexpected = {}

then we already discussed that it doesn't use the ctor you might expect it
to use (but the one from the initializer_list), so IMHO it's confusing. And
between the two remaining variants, I prefer the one with "{}" for
consistency with the other cases.


GC> And how about ctor initializer lists?

 Generally speaking, they should be used less often in C++11 because member
initialization is preferable, whenever possible.

GC> In HEAD at this moment we have:
GC> 
GC> struct ValueInterval
GC> {
GC>     ValueInterval();
GC> 
GC>     double        value_number;
GC>     std::string   value_keyword;
GC>     bool          value_is_keyword;
GC>     int           begin_duration;
GC>     duration_mode begin_mode;
GC>     int           end_duration;
GC>     duration_mode end_mode;
GC>     bool          insane;
GC> };
GC> 
GC> whose ctor follows my old practice of eliding non-POD members for which
GC> default values are wanted, so std::string 'value_keyword' is omitted here:
GC> 
GC> ValueInterval::ValueInterval()
GC>     :value_number     (0.0)
GC>     ,value_is_keyword (false)
GC>     ,begin_duration   (0)
GC>     ,begin_mode       (e_duration)
GC>     ,end_duration     (0)
GC>     ,end_mode         (e_duration)
GC>     ,insane           (false)
GC> {}

 In this case I would definitely initialize all members in their
declaration, i.e.

        struct ValueInterval
        {
            ValueInterval() = default; // Unnecessary, but more clear.

            double        value_number     = 0.0;
            std::string   value_keyword;
            bool          value_is_keyword = false;
            int           begin_duration   = 0;
            duration_mode begin_mode       = e_duration;
            int           end_duration     = 0;
            duration_mode end_mode         = e_duration;
            bool          insane           = false;
        };

Whether you want to initialize value_keyword here or not is a matter of
taste, I don't think it's really necessary, but maybe it would be better to
do it just for consistency.


GC> Today I look at that as a symmetry violation, and I want to add
GC> 
GC>      :value_number     (0.0)
GC> +    ,value_keyword    WHAT?
GC>      ,value_is_keyword (false)
GC> 
GC> but how should that be initialized? Options:
GC> 
GC>     ,value_keyword    (std::string()) // #1
GC>     ,value_keyword    ("") // #2
GC>     ,value_keyword    () // #3
GC>     ,value_keyword    {} // #4
GC> 
GC> I think #1 is ugly, #2 is poor, and #3 introduces its own asymmetry
                                         ^
                                         |
                                        ~~~
 After some initial confusion, I think you meant "#4" here.

GC> if we leave the others as they are:
GC> 
GC>      :value_number     (0.0)   // parentheses
GC>      ,value_keyword    {}      // braces?
GC>      ,value_is_keyword (false) // parentheses
GC> 
GC> so should we instead change all the parentheses to braces?

 Yes, I think we should. But, of course, first we should remove all the
unnecessary initializer list elements (i.e. those that can be replaced with
just member initialization as shown above), so there might be not that many
parentheses to change.

GC> I don't think we want to make that a general rule because we might
GC> someday have a similar case that uses lmi's class glossed_string
GC> instead of std::string, and glossed_string has std::initializer_list
GC> ctor, so the meaning of 'glossed_string{}' changes from C++11 to C++17.

 Sorry if I'm missing something, but glossed_string doesn't have a ctor
from std::initializer_list and it doesn't seen desirable to add one, so
what does this prove?

GC> Therefore, I think our general rule here should be #3:
GC>   ,value_keyword    () // datatype is std::string
GC> if we want to write it at all.

 I don't think we want to write initializers for the members which are
already initialized anyhow, which includes both the members having an
initializer in their declaration and member objects with default ctor.

 Regards,
VZ


reply via email to

[Prev in Thread] Current Thread [Next in Thread]