C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Explicitly specifying default arguments

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Thu, 2 Feb 2023 10:03:20 -0500
On Thu, Feb 2, 2023 at 5:12 AM Andrey Semashev via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> On 2/2/23 06:58, Jason McKesson via Std-Proposals wrote:
> > On Wed, Feb 1, 2023 at 8:46 PM Andrey Semashev via Std-Proposals
> > <std-proposals_at_[hidden]> wrote:
> >>
> >> On 2/2/23 04:24, Jason McKesson via Std-Proposals wrote:
> >>>
> >>> It's interesting that you bring up perfect forwarding. Because
> >>> forwarding pretty much kills this idea (and most forms of named
> >>> parameters.
> >>>
> >>> That is, if you want `T t(1, default, 5);` to work, how do you propose
> >>> to make `optional<T> t(std::in_place, 1, default, 5);` work? Or better
> >>> yet, `std::thread thr(some_functor(), 1, default, 5);`?
> >>
> >> I do not propose to make that work. The proposal is just to be able to
> >> write "default" for arguments that have a default, nothing more. This is
> >> not intended to work across an abstraction layer such as std::thread,
> >> std::bind, std::optional, function pointers, etc.
> >>
> >>> The closest I could come up with is that the expression `default` is a
> >>> prvalue of some `nullptr_t`-style type.
> >>
> >> `default` would not be an expression in a full sense. It would be a core
> >> language placeholder that is replaced by the default argument
> >> expression. That is:
> >>
> >> template< typename T >
> >> void foo(T const& str = "Hello");
> >>
> >> foo(default); // literally equivalent to foo("Hello");,
> >> // which makes T be char[6].
> >>
> >> Just to be clear, if `default` is used for an argument without a
> >> default, the program is ill-formed (same as when you don't specify an
> >> argument that doesn't have a default).
> >>
> >>> Which opens up a whole new can of worms regarding overload resolution
> >>> and function selection. Plus, there's the fact that "has a default
> >>> parameter" is not an innate property of a function; it's just
> >>> something that a particular declaration happens to have.
> >>
> >> Per the above, overload resolution is unaffected, since the call is
> >> functionally equivalent to the caller not specifying the argument at all.
> >>
> >>> I would argue that if you cannot solve the forwarding problem for
> >>> default parameters, then you're basically un-perfecting perfect
> >>> forwarding (not that it's actually perfect as is, but it's not nearly
> >>> this bad). And that's not worth the gains of being able to default
> >>> parameters.
> >>
> >> I'm not sure why you think that default argument values need to be
> >> forwarded - this is not possible now, and it was not my intention to
> >> support it with this proposal.
> >
> > You can combine forwarding and default parameters just fine today:
> >
> > ```
> > struct C
> > {
> > C(int, float = 5.0f);
> > };
> >
> > std::optional<C> t(std::in_place, 2); //Second parameter is defaulted.
> > ```
>
> The last parameter not defaulted in `std::optional<C>` constructor, that
> is, its signature does not have a `float` argument. In this sense, it is
> not forwarded, and it stays that way with the proposed addition of
> explicit `default` arguments. Yes, you cannot use explicit `default` in
> a case like this, but I don't see a problem with it.

You don't see a problem with `T t(5, default);` being legal but
`optional<T> t(in_place, 5, default)` not being legal? In essence,
you're saying that the following, or any equivalent, is just not
reasonable code to write:

```
template<typename T>
auto passthru(int i) requires requires { T t(5, default); }
{
  return optional<T>(in_place, 5, default);
}
```

Just from a feature usability standpoint, you consider it acceptable
that `make_shared`, `make_unique`, `thread`, and dozens of similar
functions are just completely incompatible with this feature, despite
default parameters as they currently exist working with all of them
presently.

>
> > If you're creating a new mechanism for calling functions, it ought to
> > be able to work with forwarding too. And if it can't be forwarded,
> > then maybe it doesn't need to exist.
> >
> > Or to put it another way, forwarding matters *way more* than
> > defaulting parameters.
>
> I don't think it is fair to say one feature is more important than the
> other, as those are distinct features that are used in different
> contexts and each one is useful on its own. That is, we use default
> arguments without forwarding and forwarding without default arguments
> all the time, and I don't see why improving default arguments without
> also benefiting forwarding is such a bad idea.

A good language is one that makes sense, where features interact in
reasonable ways. C++ doesn't always do this, but it is an unalloyed
good. We don't *always* use forwarding with default arguments, but
that option is *always available* should we need it.

You want to change the language so that it is only conditionally
available. That is... short-sighted at best. Especially since the
particular way in which you want to change it can *never* be corrected
to resolve the problem.

Received on 2023-02-02 15:03:57