On Mon, Jul 20, 2020 at 5:27 PM codusnocturnus via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Monday, July 20, 2020 1:43 PM, Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
> On Mon, Jul 20, 2020 at 2:23 PM codusnocturnus via Std-Proposals
> std-proposals@lists.isocpp.org wrote:
>
> > Having the code-bloat associated with defining and naming a struct, as well as pulling in std::optional all over the place to get the equivalent of named parameters is objectively terrible (I think you even questioned the viability), and, subjectively, it looks awful at the call site, too.
>
> Or, you could just not use named arguments. [...]
> if you can avoid having functions take large numbers of parameters
> to begin with, then you don't really need them.

Sure, a function with lots of (especially defaulted) parameters is usually a code smell, but not always.

That argument mostly only holds for greenfield and personal projects anyway.  Some of us live in the world of (very dirty) brownfield projects, and named parameters would provide a much easier way to make legacy code more maintainable than trying to get budget to reach the perfect abstraction.

FWIW, I think this is overly dismissive (in the logical sense, not the tone sense). Given the choice between a refactoring that uses only the available C++17 tools, like taking
    foobar(a, b, f, e, c, d);
into
    AB ab = foo(a, b);
    bar(ab, f, baz(e, c, d));
, and a refactoring that uses a [hypothetical] C++2b feature such as
    foobar(theA: a, theB: b, theF: f, theE: e, theC: c, theD: d);
I don't believe that the latter will necessarily require less "budget" than the former.
Sweeping generalization alert: I think it's easy to fall into the trap of thinking "The current solutions seem awkward and bad, so, a new solution will naturally be better and cheaper." Especially in the world of dirty brownfield projects, the cost of the new solution (in compiler upgrades, in training, in code-review friction) is going to be at least as high as just "making do" with what you've already got.


...
> > would make function overloading and default parameters as useable in C++ as they were intended to be. These are features that distinguish C++ from C, but today (on this very thread) we have notable language experts actively campaigning against their use!
>
> We do? I've heard people say that default arguments should be avoided,
> but I've never heard any "notable language experts" saying that we
> should avoid function overloading in general.

I may have overstated and/or miscontextualized, but I'm pretty sure one of Arthur's suggestions involved "un-overloading" a function.
 (He's active here and at C++ conferences, so I consider him both notable and an expert.)  Granted, that was not a general "don't overload" suggestion - more of a "don't inappropriately overload and then use defaults."  I think poor use of default arguments is often related to function overloading, as this case points out.

(Thanks!) Yes, that was advice against inappropriate overloading, not against overloading in general. I always point to Titus Winters' talks on overload set design:
https://www.youtube.com/watch?v=xTdeZ4MxbKo&t=4m
We may be about to prove him wrong when he says that "...it turns out there's actually very solid agreement amongst all of the experts on what is good and reasonable here" ;) but I would argue that when you have lots of parameters, and especially when you have defaulted parameters which may-be-there-or-not, it becomes very easy to violate the Google guideline
https://www.youtube.com/watch?v=xTdeZ4MxbKo&t=6m15s
"Use overloaded functions only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called."
It strikes me just now that this guideline could probably be rephrased as "A good function call is one where the reader knows roughly what's going to be done with each argument and what's going to happen to its value."  (This rephrasing even encompasses other style rules, such as "Name functions after their purpose," "Name functions with verb phrases," and "Pass out-parameters by pointer, not by lvalue reference.")

If
    draw(42);
    draw(42, mode: -1);
is good by this criterion, then
    draw(42);
    draw_with_mode(42, -1);
is at least equally good (and probably better, in an industry context where you don't want to re-train your people in the new style).

[
"Ah, but what if I want to make a forwarding template `draw_with_args(Args... args) { x.draw(args...) }`? Then it's important that these two functions share the same name!"  Possible replies:
(1) Okay, that's a perfectly valid physical reason for them to share the same name, which trumps the style guideline above; but note that now you're making a tradeoff, giving up clarity for a physical necessity.
(2) Okay, but now you have to figure out how to perfect-forward named arguments, which is going to make your WG21 proposal a lot more complicated. Adding named parameters is unlikely to be simple, or it would have been done by now! Figuring out the forwarding story is just one of the many rabbit-holes involved.
(3) Frame challenge. Instead of passing the arguments with which to draw, just pass a lambda that does the drawing however you want it to be done. This is more flexible; it frees you up to make complicated drawing lambdas or even named functions like `draw_with_mode`, and you aren't forced to shove them all into the same overload set. The STL's `emplace` commits this sin w.r.t. the constructor overload set, and I wish it didn't.
]

–Arthur