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: Greg Chicares
Subject: Re: [lmi] Default values for default arguments
Date: Sat, 11 Feb 2017 18:30:03 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Icedove/45.6.0

On 2017-02-11 16:59, Vadim Zeitlin wrote:
> 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> > 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 = {}

That is indeed what I meant to write.

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

Okay.

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

Yes, of course--I just hadn't thought of that. Old habits die hard.

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

I was motivated to bring this example up for the exact reason that
declaring eight data members and initializing all but the second
of them *elsewhere* seems so inconsistent: it makes one wonder
whether the omission was an oversight. And an accidental omission
is made more likely by declaring in one file and initializing in
another file.

OTOH, your proposal above doesn't raise the same question. It
cannot be that the author mistakenly copied some declared names,
but not all, from '.hpp' to '.cpp' because this is just one file.
I prefer to write
>           double        value_number     = 0.0;
explicitly rather than rely on some language rule that might
make that zero redundant--especially after reading that this
would be subject to aggregate initialization in C++14 but not in
C++11 (N3605, N3653). Writing "= 0.0" doesn't make the compiler's
job any harder, and I'm willing to type a few extra characters
for peace of mind and documentation.

However, in the case of the std::string member, I don't want to
repeat the type:
>           std::string   value_keyword; // Perfectly clear.
>           std::string   value_keyword = std::string(); // Annoying.

If OTOH we actually desire a C++98 ctor-initializer (as we
conceivably might in some other circumstance)...

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

Yes, sorry for the mistake. You have again divined by true intention.

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

Okay. If we prefer braces here, then we must bear in mind that they
might do surprising things depending on
 - C++11 vs C++14
 - whether or not the type has a std::initializer_list ctor
and so...

> 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
                                                    ^ insert "no"
> 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?

Nothing, because after I look back on my notes:

N3337: C++11 [5.17/9]   "The meaning of x={} is x=T()"
N4618: C++17 [5.18/9.1] "The meaning of x={} is x=T{}"

I conclude that the dialect change doesn't affect class glossed_string.

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

Good. Thanks.




reply via email to

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