Named Parameters for C++

1,428 views
Skip to first unread message

Justin Bassett

unread,
Aug 16, 2018, 1:10:53 AM8/16/18
to std-pr...@isocpp.org
I'm working on a paper to write this up more formally, but before I do that, I wanted to share my research and work and generate some discussion.

Currently, it is possible to pass parameters by position, but not by name. To work around this limitation, there are a multitude of solutions. A few are listed here:
  • The "Named Parameters Idiom," sometimes called the Builder Pattern.
  • Boost Parameters. Overloads operator= to make it look like a named parameter.
  • Boost Graph's named parameters. Parameter values are passed via member functions.
  • Strong types. This suffers from the limitation that parameters cannot be reordered, and sometimes you'd have to create types that otherwise have no meaning.
  • Designated Initializers. There are those who hope that designated initializers will work as named parameters, but they don't work that well.
Each of these have disadvantages. Here's a small list:
  • Hard to maintain.
  • Have to prefix every argument with the library's namespace, or else use a lot of using declarations or even a using directive.
  • Each argument has to be passed in order (Strong types), otherwise it's a ton of overloads for the library author to work out.
  • Possibly worse code-gen
I claim we should standardize a way of passing named arguments in C++.



Use cases for Named Parameters (aka Named Arguments, aka Keyword Arguments):
  • Named-or-positional parameters.
  • Name-only parameters
  • Either of the above combined with default arguments


Syntax: there are two possible syntaxes I've seen that work for named parameters in C++:

foo(.name = value)
foo(name: value)

I honestly don't care which we go for, but for consistency with designated initializers (which may be a bad thing if the behavior is significantly different), I'm going to choose the foo(.name = value) syntax for the time being.

For declaring a function with named parameters, it is imperative that it is opt-in, otherwise, functions parameter names become part of the public API of the function, which is undesirable:

int foo(int .x);

It might be tempting to separate the name of the parameter from the name used in named parameters, but I claim this overly repetitive. As long as named parameters are opt-in, having to give an alternative name is completely redundant. (I believe Swift does this)

There should be some way to specify that the parameter is name-only. For example, altering Python's implementation:

// anything after the . is name-only
int foo(., int .x);

Default arguments should be able to work as they are now.



Semantics:
It's a pretty standard rule that all named arguments must come after all positional arguments. It is also pretty standard that named arguments can be in a different order than in the function declaration. I don't see a compelling reason to break from this, even though there are exceptions in languages today (e.g. C# and Swift)

There are some options on how the named parameters should work. It could work like C# and just reorder the arguments. However, I believe that the name should be part of the function type and should be mangled into the name for the function. It should not be allowed for the following two declarations to coexist:

int foo(int .x, int .y);
int foo(int .a, int .b);

If the name of the parameters are not part of the function, then if those declarations are in separate translation units, there's little way to enforce that this should fail to compile / link.

This does bring in the issue that it becomes harder to change existing APIs to accept named parameters in an ABI compatible manner. For this, maybe the compiler could emit two symbols for the named-positional argument case? Otherwise, I think there should be some way to either cause the compiler to emit two symbols or otherwise maintain ABI compatibility.

Now for templates. In order to have higher-order functions which work on named parameters, I believe it is necessary to have a new kind of template parameter, something like this:

// I separated the name completely from the type.
// This would severely complicate generic code, however, so maybe there's
// a way to keep both together
template <typename F, .typename Name, typename Arg>
void call(F fn, Arg&& .Name arg) {
    // Some syntax to expand the name is needed. I'm not sure what
    // a reasonable choice is. There should also probably be a way
    // to generate a name from a compile time string.
    fn(.(Name) = std::forward<Arg>(arg));
}

// Or maybe something combining the name and type would be better:
template <typename F, .typename Kwarg>
void call(F fn, Kwarg&& arg) {
    fn(.(nameof(Kwarg)) = std::forward<Kwarg>(arg));
}

// Valid ways to call it:
call(sqrt, .x = 9);
call(sqrt, 9); // We didn't enforce name-only in the declaration of call

Reflection could possibly allow us to inspect the actual name supplied (useful for libraries such as fmt: fmt::format("({x}, {y})", .x = 10, .y = 20); ), and it would probably be useful to be able to generate a name from a compile-time string.

How lookup is performed: it's certainly possible to have overloaded functions along with named parameters. I haven't spent the time yet to flesh out all the details, but the named parameters can be each in their own dimension, as compared to the positional dimension.



Further ideas: I don't want to propose this yet, but I would also like to see named parameters in template parameters. For example, it would be nice to be easily able to specify the allocator of an unordered_map: std::unordered_map<Key, Value, .allocator = MyAllocator> . I believe this would break ABI compatibility, though.



Implementation:
It sounds crazy to add a new kind of template parameter and include the name of the parameters as part of the function type, but I believe this would be implementable with some kind of __name<"parameter_name"> type with some connection to the regular template type, as well as some rules on reordering (consider something like sorting all name-only parameters lexicographically and re-mapping named-positional parameters to their position). I won't know for sure until I attempt an implementation, but I believe it should work.



References - some things I've found about named parameter design, which I may have referred to in my writeup above. Most of these I didn't link to inline:







Nicolas Lesser

unread,
Aug 16, 2018, 1:55:29 AM8/16/18
to std-pr...@isocpp.org
For declaring a function with named parameters, it is imperative that it is opt-in, otherwise, functions parameter names become part of the public API of the function, which is undesirable

Pardon my ignorance, but why exactly is this undesirable? Designated initializer lists are also not opt-in. I don't really see a problem with this. I can't imagine function parameters to change once they're the function is part of the API.

There should be some way to specify that the parameter is name-only. For example, altering Python's implementation:

// anything after the . is name-only
int foo(., int .x);

Default arguments should be able to work as they are now.

I disagree with your proposed solution. Name-only parameters naturally appear when they are after a varadic ("catch-all") parameter, just like in Python's implementation. 

template <typename ...T>
void f(T ..., int name);

f(.name = 1); // name-only argument

Now you have a small problem: C's variadic parameters are specified in the grammar itself to come last, which can be changed but would require some restructuring. But I don't think it's worth supporting them. 

However, I believe that the name should be part of the function type and should be mangled into the name for the function. It should not be allowed for the following two declarations to coexist:

int foo(int .x, int .y);
int foo(int .a, int .b);

If the name of the parameters are not part of the function, then if those declarations are in separate translation units, there's little way to enforce that this should fail to compile / link.

Any reasons to disallow this? I mean, this is already possible:

void foo(int = 1);

void f() {
  void foo(int = 2);
  foo();
}

I'm not saying that this is good code, but the truth is that indeed there is no way to enforce this requirement over multiple TUs for every single case. There is the possibility of making this IF;NDR, but really, I don't see why I shouldn't be able to rename parameters (coding/style guidelines?).
 
Now for templates. In order to have higher-order functions which work on named parameters, I believe it is necessary to have a new kind of template parameter, something like this:

Oh I don't like this; it's too different if you know what I mean. What do you think of saying that the named parameter is implicitly part of the expansion if the expansion happens in a function parameter list?

template <typename ...Ts>
auto f(Ts ...Args) {                             // No change here.
  g(Args...);                                         // Same.
  return {Args...}[0];                          // Same.
}

f(1, 2); // ok: calls g(1, 2); returns 1
f(.Lhs = 1, .Rhs = 2); // ok; calls g(.Lhs = 1, .Rhs = 2); returns 1

For example, it would be nice to be easily able to specify the allocator of an unordered_map: std::unordered_map<Key, Value, .allocator = MyAllocator> . I believe this would break ABI compatibility, though.

How would this break ABI compatibility? The name of the function arguments need to to be mangled (which is a non-issue for templates in any case).  
 
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAPuuy5eBx1ybcPjS6dNsk7n1_c2uhdRBS19qr%3Dq5cj3cTURnLQ%40mail.gmail.com.

Justin Bassett

unread,
Aug 16, 2018, 2:34:09 AM8/16/18
to std-pr...@isocpp.org
On Wed, Aug 15, 2018 at 10:55 PM Nicolas Lesser <blitz...@gmail.com> wrote:
For declaring a function with named parameters, it is imperative that it is opt-in, otherwise, functions parameter names become part of the public API of the function, which is undesirable

Pardon my ignorance, but why exactly is this undesirable? Designated initializer lists are also not opt-in. I don't really see a problem with this. I can't imagine function parameters to change once they're the function is part of the API.

Consider all the standard library functions. Suddenly all the parameter names become public. Here's an example from this reddit thread (https://www.reddit.com/r/cpp/comments/5mdes5/what_happened_to_the_named_parameter_proposal/):

std::max(._Left = 1, ._Right = 2)

Those names are not intended to be public. They are named strangely like this to avoid problems with user defined macros. We could standardize names for every function in the standard, but I believe that is a lot of work.

Also, that would make parameter names part of the API by default. I believe library authors would like the opt-in design, as it makes it much easier to make API compatible changes. Names used as function parameters are not generally expected to be part of the API. Especially considering that there is a lot of code already written which assumes that the parameter names are not part of the API. If we suddenly change it to be part of the API, the library authors had no say in whether those names should be part of the API.
 
There should be some way to specify that the parameter is name-only. For example, altering Python's implementation:

// anything after the . is name-only
int foo(., int .x);

Default arguments should be able to work as they are now.

I disagree with your proposed solution. Name-only parameters naturally appear when they are after a varadic ("catch-all") parameter, just like in Python's implementation. 

Good point. I don't disagree with this, but I didn't like having to specify the types for the variadic template pack. Note that PEP 3102, which I linked to, allows Python to leave off the name for the variadic parameter. I was imagining something like that
 
template <typename ...T>
void f(T ..., int name);

f(.name = 1); // name-only argument

Now you have a small problem: C's variadic parameters are specified in the grammar itself to come last, which can be changed but would require some restructuring. But I don't think it's worth supporting them. 

I agree here. If supporting C's varargs is difficult in this case, I don't consider it a big drawback to disallow them when a function has named parameters.
 
However, I believe that the name should be part of the function type and should be mangled into the name for the function. It should not be allowed for the following two declarations to coexist:

int foo(int .x, int .y);
int foo(int .a, int .b);

If the name of the parameters are not part of the function, then if those declarations are in separate translation units, there's little way to enforce that this should fail to compile / link.

Any reasons to disallow this? I mean, this is already possible:

My first reason for doing so was to directly address concerns in a reddit thread asking about a previous named parameters proposal. I also believe it is wrong for names which are part of the API to be easily changeable like that. Additionally, consider name-only parameters. If they could be renamed, that would be dependent on the order in which they are declared, which I consider to be an implementation detail. I'm okay if it is allowed, but I strongly prefer it to be disallowed.
 
void foo(int = 1);

void f() {
  void foo(int = 2);
  foo();
}

Named parameters are very different from default parameters. I dislike that we can even do this for default arguments. I'd rather be more restrictive and find we should loosen the restriction later than be less restrictive and find we should have tightened the restrictions.

I'm not saying that this is good code, but the truth is that indeed there is no way to enforce this requirement over multiple TUs for every single case. There is the possibility of making this IF;NDR, but really, I don't see why I shouldn't be able to rename parameters (coding/style guidelines?).

I believe it is possible to enforce this over multiple TUs in some basic cases, but thinking about it, I guess it is true that it doesn't work for some of the more complex cases. If we mangle the names of every named parameter into the symbol, though, that would catch many cases, I believe, albeit with a not-very-descriptive diagnostic. IF;NDR is my preference otherwise.
 
Now for templates. In order to have higher-order functions which work on named parameters, I believe it is necessary to have a new kind of template parameter, something like this:

Oh I don't like this; it's too different if you know what I mean. What do you think of saying that the named parameter is implicitly part of the expansion if the expansion happens in a function parameter list?

I thought about this. My primary concern is that functions which were never designed with named parameters in mind suddenly can be called with named parameters. I believe it is best to give library authors the chance to think about this new aspect of the API before opening it up to all named parameters.

Other things:
  • This would be similar to if Python's varargs accepted kwargs as well. I don't like this.
  • How would the library author restrict a function to only named parameters, or only positional parameters?
  • There would be quite a bit of work figuring out all the cases where a name should be generated and where a name should not be generated. It might not even be possible.
 
template <typename ...Ts>
auto f(Ts ...Args) {                             // No change here.
  g(Args...);                                         // Same.
  return {Args...}[0];                          // Same.
}

f(1, 2); // ok: calls g(1, 2); returns 1
f(.Lhs = 1, .Rhs = 2); // ok; calls g(.Lhs = 1, .Rhs = 2); returns 1
 
For example, it would be nice to be easily able to specify the allocator of an unordered_map: std::unordered_map<Key, Value, .allocator = MyAllocator> . I believe this would break ABI compatibility, though.

How would this break ABI compatibility? The name of the function arguments need to to be mangled (which is a non-issue for templates in any case).  

It depends on how it is implemented. True, it only has to be a reordering, in which case it would be fully ABI compatible. For some reason, I was imagining the names being mangled into the type.

TONGARI J

unread,
Aug 16, 2018, 2:54:53 AM8/16/18
to ISO C++ Standard - Future Proposals
Yeah, I see you linked to my early draft, and it's actually been extended further to support perfect forwarding of designators.
Well, I didn't write a new version of the draft, however, some idea can be found in this thread.

The "crazy new kind of template parameter" you mentioned is implemented in my prototype and it's called "template declname parameter".
It's not restricted to named parameters. See the above thread if you're interested.

I didn't make designator part of the function type, but things got complicated after the introduction of designating-type (i.e a combination of type and designator, e.g. "int.a").
The designating-type is a pseudo type, you can't use it to declare a variable, it's only purpose is for template to decompose it.
Now a function has 2 types - one without designators and one with designators

Consider the following:

void f(int.a, int.b);

template<class F>
void call1(F f);

template<class R, class... A, declname... N>
void call2(R(*f)(A.N...));

call1
(f); // call1 is void(void(*)(int, int))
call2
(f); // call2 is void(void(*)(int.a, int.b))

The designated version is only used if you ask for explicitly, so call1 gets the plain-old-function-type, while call2 gets designated-function-type.

There's also an idea I didn't explored but probably nice to have - allowing the parameters to have 2 names (interface & impl), e.g.

template<class T>
void copy(T.from a, T&.to b) {
    b
= a;
}

mihailn...@gmail.com

unread,
Aug 16, 2018, 3:28:50 AM8/16/18
to ISO C++ Standard - Future Proposals
Note that, because of Python and C# popularity, this has been suggested multiple times in one for or another. Never passed. 

Also you missed some proposals http://jamboree.github.io/designator-draft.html 

The latter did not pass and is turned into the poor-mans http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0671r2.html

In other words, you should state what is different.

I am personally pro named arguments, but the chances are (extremely) low. 
I have written a post here, just 10 days ago, on a (more or less) alternative solution - https://groups.google.com/a/isocpp.org/d/msg/std-proposals/wz0a7NrwnGQ/iBpN8pYMFQAJ

In any case 
- C#-like and Python-like solutions have failed already. 
- Solutions that modify the signature and/or the typesystem are doomed for multiple reasons.  
Also there are people that find named arguments completely unneeded and such people might as well be in the committee. 






David Brown

unread,
Aug 16, 2018, 7:54:14 AM8/16/18
to std-pr...@isocpp.org
On 16/08/18 07:10, Justin Bassett wrote:
> I'm working on a paper to write this up more formally, but before I do
> that, I wanted to share my research and work and generate some discussion.

I am at a loss to why people key wanting to make this such a complicated
issue.

Let's simplify things, and look at the requirements:

1. We want to be able to use named parameters. It leads to clearer code
and fewer errors - especially for functions with several parameters.

2. The primary use is to check that parameters are given in the right
order, with the ability to re-order parameters at the call site as a
secondary concern.

3. It should not require changes to existing code, new ABIs, new object
file formats, or anything more than minimal changes to the compiler
front-end.

We do /not/ want this to be part of a function's signature - there is no
benefit to it, and vast complications involved. In most well-written
code a function (if exported) is declared once in a header somewhere,
and this declaration is available when the function's definition is
seen. So checking for matching parameter names can be done then - just
as checking is done for matching numbers and types. Compilers can warn
on mismatches. And if the declaration and definition are independent,
then it's the programmer's responsibility to make sure they work
together - just as for other aspects of functions.

(I appreciate that there are advantages in allowing names to be part of
the signature - it would let you distinguish "complex(double r, double
i)" and "complex(double r, double theta)". But the added complexity and
implementation effort needed would not be worth it - such costly ideas
hinder the adoption of the main feature. And there would be nothing to
stop this being added in some way at a later date.)


Named parameters are only relevant to function /calls/ - not to function
definitions. The names to use come from a function's declaration - the
argument names in a function's definition are irrelevant (though a
compiler warning to check they match would be a useful implementation
feature). They are very similar, therefore, to default arguments.

int foo(int a, int b, int c = 3, int d = 4);

All these mean the same thing:

foo(1, 2, 3, 4);
foo(1, 2, 3);
foo(.a = 1, .b = 2, .c = 3, .d = 4);
foo(.b = 2, .a = 1, 3);
foo(.d = 4, .b = 2, .a = 1);


These are errors (for obvious reasons):

foo(.ab = 1, 2);
foo(.c = 3, 2, 3);


Within the call parameters, the compiler will take the list of calling
parameters and match it up with a list of parameters in the function
declaration. First, any named parameters are put in the right spot
according to name. Then any unnamed parameters are put in the same spot
number as they had in the call. Any empty spots are filled with the
default values. If any spot has no value, or more than one value, it's
an error.


If a function is declared with some unnamed parameters, and re-declared
with names for some of these parameters, that's fine - the current set
of names is the union of these. If a function is redeclared giving the
same name to parameters, that's fine too. If a function is redeclared
and there is a conflict in a parameter name, or if a parameter is never
given a name, then that parameter can't be used as a named parameter.
An exception would be that parameter names with reserved identifiers are
considered anonymous - you can't match on such a name.

An alternative resolution here would be simply to say that the last seen
declaration is the one that counts in regard to parameter names.
(Again, compiler warnings for conflicts, excluding reserved identifiers,
would be encouraged.)



That is /all/ that is needed. It could be implemented simply (well,
relatively simply!) in any C++ compiler front-end. It does not conflict
with existing syntax, it will not change the meaning of any existing
code. New headers can be written to take advantage of the feature, but
such functions can still be used with positional parameters.

The same feature can also be used for template arguments.





mihailn...@gmail.com

unread,
Aug 16, 2018, 9:08:23 AM8/16/18
to ISO C++ Standard - Future Proposals
There are tree major problems with such simple approaches 

First - you make something, that has been private for 30+ years, public.
Second - warnings are not good enough. There is no situation where wrong argument is ignorable and not an error. If you make named arguments that weak, one can just use comments.
Third - perfect forwarding must work. 

Matthew Woehlke

unread,
Aug 16, 2018, 10:34:58 AM8/16/18
to std-pr...@isocpp.org, David Brown
On 2018-08-16 07:53, David Brown wrote:
> I am at a loss to why people key wanting to make this such a complicated
> issue.
> [...]
> 2. The primary use is to check that parameters are given in the right
> order, with the ability to re-order parameters at the call site as a
> secondary concern.

...probably because plenty of people disagree with this point. AFAIAC,
any proposal that does NOT allow these:

void foo(int, int :a = 1, int :b = 2, int :c = 12);
foo(3, b: 5); // foo(3, 1, 5, 12)

void bar(int :a);
void bar(int :x); // distinct overload

...is pointless. Checking argument order is, IMHO, not only *not* "the
primary use", but *optional*.

> 3. It should not require changes to existing code, new ABIs, new object
> file formats, or anything more than minimal changes to the compiler
> front-end.

While I agree we'd like to obtain this, I don't think it's realistic...
which probably has a lot to do with why no proposal has (yet) been accepted.

> We do /not/ want this to be part of a function's signature - there is no
> benefit to it,

Wrong: overloading.

Python doesn't have this problem because a) there is no such thing as
separate declarations and definitions in Python, and b) Python has no
overloading, period.

These aren't the case in C++, and trying to ignore the ramifications of
these points as they apply to named arguments is IMO a recipe for failure.

Also, forwarding needs to work somehow. I don't see how this can be done
without making names part of the type system.

> (But the added complexity and implementation effort needed would not
> be worth it - such costly ideas hinder the adoption of the main
> feature. And there would be nothing to stop this being added in some
> way at a later date.)

I disagree; if you make named arguments *not* part of the ABI, I think
it will be very hard to go back and change that decision.

> foo(.b = 2, .a = 1, 3);

This is confusing, and IMO should be prohibited. Just like it is in Python.

--
Matthew

David Brown

unread,
Aug 16, 2018, 10:53:50 AM8/16/18
to Matthew Woehlke, std-pr...@isocpp.org
On 16/08/18 16:34, Matthew Woehlke wrote:
> On 2018-08-16 07:53, David Brown wrote:
>> I am at a loss to why people key wanting to make this such a complicated
>> issue.
>> [...]
>> 2. The primary use is to check that parameters are given in the right
>> order, with the ability to re-order parameters at the call site as a
>> secondary concern.
>
> ...probably because plenty of people disagree with this point. AFAIAC,
> any proposal that does NOT allow these:
>
> void foo(int, int :a = 1, int :b = 2, int :c = 12);
> foo(3, b: 5); // foo(3, 1, 5, 12)

My proposal allows that. It just doesn't need any change to the
declaration to make it work:

void foo(int, int a = 1, int b = 2, int c = 12);
foo(3, b: 5); // foo(3, 1, 5, 12)

(I suggested the syntax "foo(3, .b = 5)" - but I would be perfectly
happy with alternatives like "foo(3, b: 5)".)


>
> void bar(int :a);
> void bar(int :x); // distinct overload

> ...is pointless.

I disagree. Distinct overloads like this would be nice, but (as I
wrote) extremely costly in terms of changes to implementations and
existing software. I would rather see that as a later enhancement once
we have the simple named parameters in place - my proposal does not
conflict with having this as a later feature.

>
> Checking argument order is, IMHO, not only *not* "the
> primary use", but *optional*.

Checking argument order is absolutely the key point in terms of helping
people write correct code by spotting errors at compile time. It is
also key to making code clearer by documenting parameters better without
ugly "/* parameter_name */" comments.

Those would be two /huge/ benefits to today's language, and can be
implemented easily and cheaply.

Allowing a re-arrangement of the order of parameters would be another
big benefit for not much higher cost, and is also something I would want.

The cost-benefit relation for name-based overload is very different.
While I would also like it, I would hate to see simple, useful named
parameters blocked or delayed while people fight over this feature.

>
>> 3. It should not require changes to existing code, new ABIs, new object
>> file formats, or anything more than minimal changes to the compiler
>> front-end.
>
> While I agree we'd like to obtain this, I don't think it's realistic...
> which probably has a lot to do with why no proposal has (yet) been accepted.

My proposal would not require any changes to these things. The reason
no proposal has been accepted is because people demand too much - such
as name-based overloads - that block the simple features.

>
>> We do /not/ want this to be part of a function's signature - there is no
>> benefit to it,
>
> Wrong: overloading.

Yes, you are right - you need the names to be part of the signature for
name-based overloading. That is a key reason why name-based overloading
should not be part of a first step for named parameters - it avoids this
massive change.

>
> Python doesn't have this problem because a) there is no such thing as
> separate declarations and definitions in Python, and b) Python has no
> overloading, period.
>
> These aren't the case in C++, and trying to ignore the ramifications of
> these points as they apply to named arguments is IMO a recipe for failure.
>
> Also, forwarding needs to work somehow. I don't see how this can be done
> without making names part of the type system.
>

It is very simple - as long as you drop name-based overloading. Named
parameters can be based purely and completely on function declarations -
nothing else. If you want to forward to different function, use the
names declared for that function. If you want to use a function
pointer, use the names given in the declaration for that function
pointer. If there are no names given, you can't use named parameters.

>> (But the added complexity and implementation effort needed would not
>> be worth it - such costly ideas hinder the adoption of the main
>> feature. And there would be nothing to stop this being added in some
>> way at a later date.)
>
> I disagree; if you make named arguments *not* part of the ABI, I think
> it will be very hard to go back and change that decision.
>

I think it is important to consider how they might be added later, and
what effect that would have - and if possible leave the door open for
name-based overloads in future versions.

But if the choice came down to named parameters without name-based
overloads implemented now, or waiting for an all-singing all-dancing
version some time in the future, I have not the slightest doubt which I
would pick.


>> foo(.b = 2, .a = 1, 3);
>
> This is confusing, and IMO should be prohibited. Just like it is in Python.
>

I would not object to it being prohibited.


Nicol Bolas

unread,
Aug 16, 2018, 11:42:01 AM8/16/18
to ISO C++ Standard - Future Proposals
Designated initializers effectively gives you most of these features. That is, if you declare your function as taking a single aggregate of parameters, you can get about 90% of what you want.

A function with named parameters would have that set of named parameters as part of its ABI:

struct FoosParams {int x; int y;};
void foo(FoosParams params);

You would have the ability to call it with or without names, but you can't mix-and-match:

foo({.x = 5, .y = 12});
foo({5, 12});
foo({.x = 5, 12}); //ill-formed

You can have default parameters without positional order:

struct FoosParams {int x = 5; int y;}
void foo(FoosParams params);
foo({.y = 12});

It even allows meaningful overloading on "names" alone:

struct complex_ri {double r, double i};
struct complex_rt {double r, double theta_rad};

//Constructors

complex(complex_ri ri);
complex(complex_rt rt);

//usage
complex a({.r = 5.0d, .i = 3.0d});
complex b({.r = 4.2d, .theta_rad = 2});
complex c({5.0d, 3.0d}); //ill-formed, due to call ambiguity.

I believe that works with designated initializes in C++20, but I admittedly have not studied the proposal that closely. So it may not work.

This even works through forwarding, but it does require you to explicitly name the `FoosParams` type at the call site:

std::function<void(FoosParams)> foofunctor = &foo;
foofunctor(FoosParams{.x = 5, .y = 12});

Now of course, this way of handling named parameters causes a bunch of warts. Anytime you want to create such a function, you have to explicitly create a type for it. Forwarding, or any usage of the signature, requires using that typename. And while you can forward them, the forwarding function only sees it as a single struct, not a set of values. This makes doing things like transforming parameters impossible (well, without reflection, and even then, your code would have to assume that the single struct is a set of "parameters" to be transformed). And so forth.

But overall, it gets the job done without language changes. It's not clean, so it won't be used universally or anything, but it works well enough that it can be used in places where the ambiguity of a call is worse than having to pass a struct.

The way I see it, the only reason to promote named parameters to a language feature is to make usage of them more widespread. That is, rather than restricting them to cases of significant ambiguity where the cost of a struct is better than without it, you would be able to use them in cases where you don't really have to.

mihailn...@gmail.com

unread,
Aug 16, 2018, 11:45:02 AM8/16/18
to ISO C++ Standard - Future Proposals, da...@westcontrol.com


On Thursday, August 16, 2018 at 5:34:58 PM UTC+3, Matthew Woehlke wrote:
On 2018-08-16 07:53, David Brown wrote:
> I am at a loss to why people key wanting to make this such a complicated
> issue.
> [...]
> 2. The primary use is to check that parameters are given in the right
> order, with the ability to re-order parameters at the call site as a
> secondary concern.

...probably because plenty of people disagree with this point. AFAIAC,
any proposal that does NOT allow these:

  void foo(int, int :a = 1, int :b = 2, int :c = 12);
  foo(3, b: 5); // foo(3, 1, 5, 12)

  void bar(int :a);
  void bar(int :x); // distinct overload

...is pointless. Checking argument order is, IMHO, not only *not* "the
primary use", but *optional*.

Names part of the signature makes them mandatory. There are plenty of ways to do that today - types, tags, function names.
And all work with forwarding. 

I might disagree on a lot of points with the OP, but the primary goal is to confirm parameters are semantically correct when that meaning is hard, incorrect or clumsy to express as a separate type.
Bonus is defaulted-params-in-the-middle which will enable foo(3, a:, b: 5); and eventually a bonus rearrange. 

mihailn...@gmail.com

unread,
Aug 16, 2018, 12:02:09 PM8/16/18
to ISO C++ Standard - Future Proposals
Add to all this, 
 - no IntelliSense will ever help you , 
 - the implementation is severely affected (structured bindings to the rescue I guess...)
 - overloading is surreal 

Matthew Woehlke

unread,
Aug 16, 2018, 12:25:22 PM8/16/18
to std-pr...@isocpp.org, David Brown
On 2018-08-16 10:53, David Brown wrote:
> On 16/08/18 16:34, Matthew Woehlke wrote:
>> Checking argument order is, IMHO, not only *not* "the
>> primary use", but *optional*.
>
> Checking argument order is absolutely the key point in terms of helping
> people write correct code by spotting errors at compile time. It is
> also key to making code clearer by documenting parameters better without
> ugly "/* parameter_name */" comments.
>
> Those would be two /huge/ benefits to today's language, and can be
> implemented easily and cheaply.

While I don't disagree with that, exactly, I also don't see it as a
compelling benefit. For me, named arguments are *primarily* about fixing
the problem of needing extremely flexible functions (read: large number
of parameters and/or large number of overloads) of which any *one* user
is only going to care about a small slice of the total surface area. I
only care about argument order to the extent it is useful in solving
that problem. Thus, for me, *the* killer features are being able to
specify one parameter in the middle of a list of defaulted parameters,
and/or to overload on parameter names.

If I don't get those, I'd rather not muddy the feature space with
anything else.

> Yes, you are right - you need the names to be part of the signature for
> name-based overloading. That is a key reason why name-based overloading
> should not be part of a first step for named parameters - it avoids this
> massive change.
> [...]
> [Forwarding] is very simple - as long as you drop name-based overloading.
> [...]
> I think it is important to consider how they might be added later, and
> what effect that would have - and if possible leave the door open for
> name-based overloads in future versions.

Right, so... here's an experiment. Explain how we could *later* add
name-based overloading on top of your proposal.

If you can't do that, then accepting your proposal means we can *never*
have name-based overloading. Which means everyone that wants that is
going to be opposed to your proposal.

If you *can* do that, I think it would make your proposal much more
compelling.

--
Matthew

Nicol Bolas

unread,
Aug 16, 2018, 12:28:59 PM8/16/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
"And so forth" means that the list is not comprehensive. But that being said:
 
 - no IntelliSense will ever help you , 

In what way will it not ever help you? Can't intellisense work with aggregate initialization or list initialization in general? It should be easy enough for it to figure out the possible overloads and, when you open the first brace, list possibilities for designated or positional list arguments.

Maybe such tools aren't not smart enough to do that at present, but I see no reason why it would be impossible for them to provide help.

Zhihao Yuan

unread,
Aug 16, 2018, 12:52:42 PM8/16/18
to std-pr...@isocpp.org
> From: Nicol Bolas <jmck...@gmail.com>
> Sent: Thursday, August 16, 2018 10:42 AM
>
> You would have the ability to call it with or without names, but you can't mix-and-match:
>
> foo({.x = 5, .y = 12});
> foo({5, 12});
> foo({.x = 5, 12}); //ill-formed

This may be extended to at least allow
an _initializer-list_ as a prefix.

> It even allows meaningful overloading on "names" alone:
>
> complex a({.r = 5.0d, .i = 3.0d});
> complex b({.r = 4.2d, .theta_rad = 2});

This is deliberately not allowed.

> And while you can forward them, the forwarding function only sees it as a single struct, not a set of values.

Some authors believe that if we get some form of
named parameters in the future, its semantics
should be equivalent to decomposing such a struct
with a compiler synthesized structured binding.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mailto:std-proposal...@isocpp.org.
To post to this group, send email to mailto:std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/5d064165-5a58-4810-90f7-aa3732be92db%40isocpp.org?utm_medium=email&utm_source=footer.


mihailn...@gmail.com

unread,
Aug 16, 2018, 1:10:44 PM8/16/18
to ISO C++ Standard - Future Proposals, da...@westcontrol.com


On Thursday, August 16, 2018 at 7:25:22 PM UTC+3, Matthew Woehlke wrote:
On 2018-08-16 10:53, David Brown wrote:
> On 16/08/18 16:34, Matthew Woehlke wrote:
>> Checking argument order is, IMHO, not only *not* "the
>> primary use", but *optional*.
>
> Checking argument order is absolutely the key point in terms of helping
> people write correct code by spotting errors at compile time.  It is
> also key to making code clearer by documenting parameters better without
> ugly "/* parameter_name */" comments.
>
> Those would be two /huge/ benefits to today's language, and can be
> implemented easily and cheaply.

While I don't disagree with that, exactly, I also don't see it as a
compelling benefit. For me, named arguments are *primarily* about fixing
the problem of needing extremely flexible functions (read: large number
of parameters and/or large number of overloads) of which any *one* user
is only going to care about a small slice of the total surface area. I
only care about argument order to the extent it is useful in solving
that problem. Thus, for me, *the* killer features are being able to
specify one parameter in the middle of a list of defaulted parameters,
and/or to overload on parameter names.

Why overloading is so important to you? Because the first request is doable (default-by-mention or full rearrange), but the last is a nightmare to define and is doable today via workarounds. 

Can you please define names change the signature? Is this some sort of name-type pair? Is it a tag? Is this pure compiler help or part of the type system? 

And again, why is a killer?

Matthew Woehlke

unread,
Aug 16, 2018, 1:29:33 PM8/16/18
to std-pr...@isocpp.org, mihailn...@gmail.com
On 2018-08-16 13:10, mihailn...@gmail.com wrote:
> Why overloading is so important to you? Because the first request is doable
> (default-by-mention or full rearrange), but the last is a *nightmare* to
> define *and* is doable today via workarounds.

vector<int> x{values: 1};
vector<int> x{reserve: 1};

complex{imag: 1};
complex{r: 1, t: 90};

Is it *killer*? Maybe, maybe not. But that's not the point. IMHO, the
burden of proof should be on the one(s) arguing for an implementation
that seems to preclude *ever* getting name overload to either show a)
why we will *never* need/want this feature, or b) how it could still be
implemented, later.

I'd rather have no feature than a half-baked feature that can't ever be
made better.

--
Matthew

mihailn...@gmail.com

unread,
Aug 16, 2018, 2:40:03 PM8/16/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
The point was to define how it would work.

  vector<int> x{values: 1};
  vector<int> x{reserve: 1}; 

This is doable with tags. And we can improve them
 
  complex{imag: 1};
  complex{r: 1, t: 90}; 

This is already overloaded, no need for strong names.


BTW, I am not against any use case you would come up with, as I said I am pro named arguments, 
not only that, I believe we need both that change the signature and the ones that don't!

However, the ones that do change the signature are, in practice, both less common and easier to work around
And, the ones that do not need overload are more common and often very clumsy to work around.

As for future proofing -  there is no problem if we support both

rect(Point topLeft: pos, Point bottomRight: br); //< weak argument names, not mandatory 

// later in some future
 
rect(center: Point pos, Point bottomRight: br) //< strong argument name, adds an overload, not ignorable


All the old code will work, but now center: is mandatory and will overload. 

How would this work? Well, the easiest is to be a tag, as already explored. 
But it could be a modifier to Point or some automagic shortcut to std::pair<"center", Point> 

And again, weak named argument are somewhat easier and not less useful (unless they are just warnings) 
and not mutually exclusive to strong ones!



--
Matthew

Matthew Woehlke

unread,
Aug 16, 2018, 2:49:30 PM8/16/18
to std-pr...@isocpp.org, mihailn...@gmail.com
On 2018-08-16 14:40, mihailn...@gmail.com wrote:
> This is already overloaded, no need for strong names.

Eh? Both of those functions, without names have / would have the
signature (double, double). Without strong names, that would be ambiguous.

> As for future proofing - *there is no problem if we support both*
>
> rect(Point topLeft: pos, Point bottomRight: br); //< weak argument names,
> not mandatory
>
> // later in some future
>
> rect(center: Point pos, Point bottomRight: br) //< strong argument name,
> adds an overload, not ignorable

How would... oh, wait, you're proposing to differentiate strong vs. weak
names *based on position*? That's confusing as all else. I can't imagine
that would *ever* be approved.

--
Matthew

Thiago Macieira

unread,
Aug 16, 2018, 3:50:41 PM8/16/18
to std-pr...@isocpp.org
On Thursday, 16 August 2018 07:53:47 PDT David Brown wrote:
> > void bar(int :a);
> > void bar(int :x); // distinct overload
> >
> > ...is pointless.
>
> I disagree. Distinct overloads like this would be nice, but (as I
> wrote) extremely costly in terms of changes to implementations and
> existing software. I would rather see that as a later enhancement once
> we have the simple named parameters in place - my proposal does not
> conflict with having this as a later feature.

I don't think you can have this as a later feature. It needs to be designed in
from the first version.

The mangling of "void bar(int)" is
_Z3bari
?bar@@YAXH@Z

If this is used for "void bar(int :a)", then you CANNOT later overload with
"void bar(int :x)". In other words, for named arguments to be overloadable at
any point in the future, they must have been overloadable at all points in the
past too.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



mihailn...@gmail.com

unread,
Aug 16, 2018, 3:54:06 PM8/16/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


On Thursday, August 16, 2018 at 9:49:30 PM UTC+3, Matthew Woehlke wrote:
On 2018-08-16 14:40, mihailn...@gmail.com wrote:
> This is already overloaded, no need for strong names.

Eh? Both of those functions, without names have / would have the
signature (double, double). Without strong names, that would be ambiguous.

Ok I skimmed a bit too fast.

As I said, I will not deny any use case, but the workarounds are not hard and some would argue more correct in this case, like making angle a different type.
 

> As for future proofing -  *there is no problem if we support both*
>
> rect(Point topLeft: pos, Point bottomRight: br); //< weak argument names,
> not mandatory
>
> // later in some future
>  
> rect(center: Point pos, Point bottomRight: br) //< strong argument name,
> adds an overload, not ignorable

 
How would... oh, wait, you're proposing to differentiate strong vs. weak
names *based on position*? That's confusing as all else. I can't imagine
that would *ever* be approved.

Yet, it is learnable and does not require extra syntax.

That was not the point, the point is - we need both - one can't just say "named arguments are like this" and stick to only one type. 

Both are useful, but for different things 
 - weak as semantic disambiguation (for the reader) and conformation (from the compiler), where using type is incorrect/impractical/maintenance toll/code size toll, 
   and, eventually for some sort of typing shortcut
 - strong as "named constructors", almost exclusively.

Note that strong can't be used for the purpose of weak, because this will be extremely annoying (*chough* Objective C)  




--
Matthew

Nicol Bolas

unread,
Aug 16, 2018, 4:05:47 PM8/16/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


On Thursday, August 16, 2018 at 3:54:06 PM UTC-4, mihailn...@gmail.com wrote:


On Thursday, August 16, 2018 at 9:49:30 PM UTC+3, Matthew Woehlke wrote:
On 2018-08-16 14:40, mihailn...@gmail.com wrote:
> This is already overloaded, no need for strong names.

Eh? Both of those functions, without names have / would have the
signature (double, double). Without strong names, that would be ambiguous.

Ok I skimmed a bit too fast.

As I said, I will not deny any use case, but the workarounds are not hard and some would argue more correct in this case, like making angle a different type.
 

> As for future proofing -  *there is no problem if we support both*
>
> rect(Point topLeft: pos, Point bottomRight: br); //< weak argument names,
> not mandatory
>
> // later in some future
>  
> rect(center: Point pos, Point bottomRight: br) //< strong argument name,
> adds an overload, not ignorable

 
How would... oh, wait, you're proposing to differentiate strong vs. weak
names *based on position*? That's confusing as all else. I can't imagine
that would *ever* be approved.

Yet, it is learnable and does not require extra syntax.

There are a lot of things in C++ that are "learnable". SFINAE is "learnable". "Uniform" initialization is "learnable". The "Most Vexing Parse" is "learnable".

But I think we can make a distinction between things whose behavior is fairly obvious and things whose behavior requires significant learning before it can be understood. What you're talking about seems more like the latter.

That was not the point, the point is - we need both - one can't just say "named arguments are like this" and stick to only one type. 

Both are useful, but for different things 
 - weak as semantic disambiguation (for the reader) and conformation (from the compiler), where using type is incorrect/impractical/maintenance toll/code size toll, 
   and, eventually for some sort of typing shortcut
 - strong as "named constructors", almost exclusively.

Note that strong can't be used for the purpose of weak, because this will be extremely annoying (*chough* Objective C)

The question therefore is whether we need "weak semantic disambiguation and confirmation" as a language feature at all.

Strong named parameters actually do something; they allow us to express things that either couldn't be done before or were very inconvenient to do.

Weak named parameters by contrast are mere notation. So why is it important that we have a language feature for that?

Equally importantly, there are costs to having both. Two separate syntaxes for something that most languages handle with one. Two separate syntaxes that have to fight for space in the same area (function parameters). Will they be reasonably distinct from one another? Is not having two syntaxes that do subtly different things not inherently confusing?

mihailn...@gmail.com

unread,
Aug 16, 2018, 5:38:49 PM8/16/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
They fix the last semantic hole that neither Types nor Contracts can fix. 
Types bound what the item is, contracts bound what value the item has, names bound what the meaning of the item is.

Often meaning is inferred form type but often this is not possible (math, physics) or simply incorrect semantically - MotherName is wrong kind of type.
And often meaning is inferred from the variables themselves, but that is not enforceable.

Yet, passing an argument, meaning something different is, for all intend and proposes, a breach of contract, no matter if this is a form on paper to fill with a pen or a function call.  

Can this be solved by types - only if workaround is solving 
Can this be solved by strong names - well, yeah, at the price of thinking the users are idiots having them to repeat like monkeys every name that "we think it is important". 

Nicol Bolas

unread,
Aug 16, 2018, 6:08:45 PM8/16/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
Your argument makes more sense for strong names than weak ones. After all, if "meaning something different" is truly a "breach of contract" (and therefore an error), then that contract is important enough to be stated whenever the function is used. If I've written a function that needs to test this "contract", then I want everyone who calls it to actually provide the correct information.

And if the contract is not important enough to be stated explicitly, then why does it need to be said at all. How can a contract be optional? And I don't mean in the Contracts proposal way, where compiler options/`axiom` can turn them on or off. I mean in that each individual use has the ability to pick and choose whether to provide this "meaning" or not.

Or to put it another way, the only reason "override" is optional is that it would be a huge compatibility break to make it mandatory. That feature is very similar to here: both cases are stating the meaning of something explicitly.

Why is this meaning important enough to be said and verified sometimes and not other-times?

Lastly, a big problem I have with your conception of weak names is that you're trying to make them a full-fledged piece of information connected with a value (in order to make forwarding work)... but you're not using any existing C++ mechanism to provide this channel of information. Names aren't types. They aren't values. They're associated with parameter names, but they have to be able to pass through `std::forward` somehow. And users have to be able to pass them to non-named functions (like loggers) as well as named ones.

The various strong naming proposals make these names actual parts of the C++ language. Some make them into values passed into the function. Others make the function take an unnamed struct. And so forth.

Weak names can't work through normal C++ channels of information.

Can this be solved by types - only if workaround is solving 
Can this be solved by strong names - well, yeah, at the price of thinking the users are idiots having them to repeat like monkeys every name that "we think it is important".

To the degree that I think named parameters are important enough to have in the language, I will happily pay the price of the latter if it means not having to pay the price of having two syntaxes for things that are essentially the same thing.

It's bad enough that we're creating two separate names for a parameter at all (the in-function name and the out-of-function name). Let's not compound this folly by having two ways to set the out-of-function name that have slightly different meanings.

Justin Bassett

unread,
Aug 17, 2018, 12:04:21 AM8/17/18
to std-pr...@isocpp.org
On Thu, Aug 16, 2018 at 12:28 AM <mihailn...@gmail.com> wrote:
Note that, because of Python and C# popularity, this has been suggested multiple times in one for or another. Never passed. 

Also you missed some proposals http://jamboree.github.io/designator-draft.html 

The latter did not pass and is turned into the poor-mans http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0671r2.html

In other words, you should state what is different.

I wasn't aware of P0671. My proposal is similar to R0, but more different from R2. Ignoring syntax differences, P0671r0 makes named parameters opt-in and only supports name-only parameters. P0671r2 goes in a completely different direction and makes parameters named by default and named-positional, with a possible attribute for name-only. P0671 talks about ellipses, which I haven't investigated yet, but I don't believe I would stray too far from that. P0671r2 looks like nothing more than parameter reordering, which I believe to be an inferior form of named parameters. I didn't see any mention of template functions, but I believe P0671 only supports named parameters on the outermost layer. My proposal works to integrate named parameters into the language as a core language feature rather than as simple syntactic sugar on top. 
 

Justin Bassett

unread,
Aug 17, 2018, 12:04:48 AM8/17/18
to std-pr...@isocpp.org
On Thu, Aug 16, 2018 at 7:34 AM Matthew Woehlke <mwoehlk...@gmail.com> wrote:
On 2018-08-16 07:53, David Brown wrote:
> I am at a loss to why people key wanting to make this such a complicated
> issue.
> [...]
> 2. The primary use is to check that parameters are given in the right
> order, with the ability to re-order parameters at the call site as a
> secondary concern.

...probably because plenty of people disagree with this point. AFAIAC,
any proposal that does NOT allow these:

  void foo(int, int :a = 1, int :b = 2, int :c = 12);
  foo(3, b: 5); // foo(3, 1, 5, 12)

  void bar(int :a);
  void bar(int :x); // distinct overload

...is pointless. Checking argument order is, IMHO, not only *not* "the
primary use", but *optional*.

I agree for the most part, but I disagree that this should be a valid overload set, unless parameters are name-only:

void bar(int :a);
void bar(int :x);

The problem is that bar(4) is ambiguous. It would be better to catch this at the declaration rather than at the first ambiguous point of use. Overloading on name-only parameters is just fine.
 

David Brown

unread,
Aug 17, 2018, 6:14:28 AM8/17/18
to std-pr...@isocpp.org
On 16/08/18 15:08, mihailn...@gmail.com wrote:
> There are tree major problems with such simple approaches
>
> First - you make something, that has been private for 30+ years, public.

No, it is not. The names for parameters are in function declarations,
in headers - these are public. My proposal does not use names from the
definitions (which may be private).

Some functions are declared without parameter names - you can't use them
for named parameters, without a re-declaration. Some functions are
declared with "private" reserved names as parameter names (especially in
standard library implementations, to avoid conflict with any macros the
user may have defined). By my proposal, you can't use them either as
parameter names.

> Second - warnings are not good enough. There is no situation where wrong
> argument is ignorable and not an error. If you make named arguments that
> weak, one can just use comments.

I'd have no problem with errors rather than warnings.

But I would want to be able to call existing functions using named
arguments, and I would want to call new functions with positional
arguments. I am sceptical to the idea of being able to force the use of
named arguments at any point (in fact, that would, I think, cause more
problems for perfect forwarding), and the whole idea would be useless if
special syntax were needed to declare that a function uses named parameters.

> Third - perfect forwarding must work.
>

Perfect forwarding would work fine, as far as I can see. However, I
don't claim to be particularly familiar with perfect forwarding, and I
would appreciate corrections if it looks like I've got things wrong
here. I agree that perfect forwarding /should/ work - I'm less
convinced that it /must/ work. Again, I am frustrated that a simple
feature that could be extremely useful in many cases is missing merely
because it can't be used in some other cases.


Here is an example:

template<typename T, typename... Args>
unique_ptr<T> make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

And let's make a class:

class Point {
public:
Point(int x, int y, int z);
...
}

This means you can have:

int a = 1;
const int b = 2;

auto p1 = make_unique<Point>(a, b, 3);

When the compiler sees this "make_unique" call, it also has the
declaration of Point's constructor available. So if you write this:

auto p2 = make_unique<Point>(.y = b, .z = 3, .x = a);

the compiler knows the names of the parameters in the constructor, and
can re-arrange them to give exactly the same call as the positional
arguments.

Is this what you were looking for, or did you mean something else?

David Brown

unread,
Aug 17, 2018, 7:10:58 AM8/17/18
to std-pr...@isocpp.org
On 16/08/18 21:50, Thiago Macieira wrote:
> On Thursday, 16 August 2018 07:53:47 PDT David Brown wrote:
>>> void bar(int :a);
>>> void bar(int :x); // distinct overload
>>>
>>> ...is pointless.
>>
>> I disagree. Distinct overloads like this would be nice, but (as I
>> wrote) extremely costly in terms of changes to implementations and
>> existing software. I would rather see that as a later enhancement once
>> we have the simple named parameters in place - my proposal does not
>> conflict with having this as a later feature.
>
> I don't think you can have this as a later feature. It needs to be designed in
> from the first version.
>
> The mangling of "void bar(int)" is
> _Z3bari
> ?bar@@YAXH@Z
>
> If this is used for "void bar(int :a)", then you CANNOT later overload with
> "void bar(int :x)". In other words, for named arguments to be overloadable at
> any point in the future, they must have been overloadable at all points in the
> past too.
>

Of course. And that is /fine/.

If you want to have two functions that are overloaded on the basis of
their parameter names, then you want that to be part of the definition
and declaration of /both/ functions. You don't want to have one
"normal" function and one "overloaded" function - that opens the door to
ambiguity, confusion, and getting the wrong function by mistake.

Suppose we start with named parameters in the way I described. Then you
have:

// declaration
void foo(int a, int b);

// definition can use any names, but matching names recommended
void foo(int a, int param_b) { ... }

// call
foo(1, 2);
foo(.a = 1, .b = 2);


Let's add a syntax for /required/ named parameters. Unlike someone
else's suggestion, I would want this to be clearly distinguished - not
just based on a minor change in positions. At this stage, I really
don't know what syntax would be nice here, so I'm simply going to invent
one to discuss the point - it is not a suggestion for a final choice of
syntax.

// declarations
void foo(int @a, int @b);
void foo(int @x, int @y);

// definitions must use identical names
void foo(int @a, int@b) { ... }
void foo(int @x, int@y) { ... }

// call
foo(.a = 1, .b = 2);
foo(.y = 10, .x = 20);

foo(1, 2) // Syntax error!

As far as the caller is concerned, the syntax for non-optional named
parameters is the same as that of choosing to use optional named parameters.

If the mangled name of "void foo(int, int)" is "foo_v_i_i", then the
declarations and definitions above would mangle to "foo_v_i_i__a_b" and
"foo_v_i_i__x_y", or something on those lines. The compiler can
distinguish the declarations, it can distinguish the definitions, it can
distinguish the mangled names used for linkage. No information needs to
be added to the ABI, the object formats, or anything else other than
some extra mangling. No changes are needed on the linker, or most of
the compiler - just a straightforward change to the compiler front-end
(and debuggers trying to interpret the mangled symbols).


Thus you need a change to the mangled name of a function only if you
want to use parameter name based overload. Other uses of parameter
names are unaffected.




Jake Arkinstall

unread,
Aug 17, 2018, 8:36:33 AM8/17/18
to std-pr...@isocpp.org
On Fri, 17 Aug 2018, 12:10 David Brown, <da...@westcontrol.com> wrote:
Let's add a syntax for /required/ named parameters.

The named parameter topic always starts with a suggestion that named parameters make things easier for a user to read a function call site. That's something I'm willing to go along with for the sake of argument.

What I'm uncomfortable about is when it forcably changes how a user writes a function call. It doesn't make things much easier to write because memorising the parameter names is barely different to memorising the parameter orders. For me it's purely about reading the call site. I'm all for people using it for their ease, but I want the choice of not using it for mine. I know I'm not going to be creating them for a long time simply because it isn't compatible with compilers and, being a language change, there's no way of spoofing the behaviour.

I also don't like the notion of implicitly elevating a name to a type, which is what I see here. Perhaps it's the physicist in me, but for me a function operates on some types Ts..., which it is able to label purely for its own purposes. I'm comfortable with the idea that if two calls do two different things with the same types, either it isnt the same function or they aren't the same semantic types. In most cases the latter should be the case.

At least python handles the overloading issue - by not supporting overloading. But that also comes with its own problems - it encourages ridiculous interfaces. See pandas.to_csv as a good example - credit to them for making such an incredible library, but their many-optional-parameter interfaces combined with their inconsistent parameter naming styles results in a usability nightmare, and we're opening ourselves up to that mentality with the very mention of named parameters.

Anyway, rant over.

While I disagree with the reason for wanting forcably named parameters, one thing it does provide is an implementation via strong types. One that can be implemented at a higher level without changing the mangling, and without runtime overhead. If a function has a forced named parameter, such as my_func(@T x), that @T can be internally represented as a struct my_func_x { T value; }. When you call my_func(x: 3), it puts 3 into a struct my_func_x, passes that to my_func, which then automatically reinterpret_casts the my_func_x as a T* and dereferences into x. As my_func_y cant implicitly cast to my_func_x, there's no ambiguity.

David Brown

unread,
Aug 17, 2018, 10:22:00 AM8/17/18
to std-pr...@isocpp.org
On 17/08/18 14:36, Jake Arkinstall wrote:
> On Fri, 17 Aug 2018, 12:10 David Brown, <da...@westcontrol.com
> <mailto:da...@westcontrol.com>> wrote:
>
> Let's add a syntax for /required/ named parameters.
>
>
> The named parameter topic always starts with a suggestion that named
> parameters make things easier for a user to read a function call site.
> That's something I'm willing to go along with for the sake of argument.
>

No one (I hope) suggests that named parameters always make it easier to
read or write function calls correctly - merely that they are a tool
that can improve readability and help get correct code in some cases.

> What I'm uncomfortable about is when it forcably changes how a user
> writes a function call.

I agree. I want it to be optional. I can only see it be /required/ if
parameter name based overloading is used. (Overloading by parameter
name is not something I see as a big issue, or a big use case, but some
people are keen on it.)

> It doesn't make things much easier to write
> because memorising the parameter names is barely different to memorising
> the parameter orders.

IDE's will help with this, but otherwise I agree.

> For me it's purely about reading the call site.
> I'm all for people using it for their ease, but I want the choice of not
> using it for mine. I know I'm not going to be creating them for a long
> time simply because it isn't compatible with compilers and, being a
> language change, there's no way of spoofing the behaviour.

Agreed. I would be entirely against a syntax that required changes to
the definitions or declarations of functions that made them incompatible
with existing compilers - except where it supports a new feature that
was not available previously. (This would be parameter name based
overloading.)
What you are suggesting here, I think, is that forced named parameters -
as required for parameter name based overloading - could be handled as
syntactic sugar for generating new types on the fly. That does not
sound unreasonable, and could be a way to implement it using a minimum
of changes to existing tools.


Vicente J. Botet Escriba

unread,
Aug 17, 2018, 10:46:46 AM8/17/18
to std-pr...@isocpp.org, Thiago Macieira
Le 16/08/2018 à 21:50, Thiago Macieira a écrit :
On Thursday, 16 August 2018 07:53:47 PDT David Brown wrote:
void bar(int :a);
void bar(int :x); // distinct overload

...is pointless.
I disagree.  Distinct overloads like this would be nice, but (as I
wrote) extremely costly in terms of changes to implementations and
existing software.  I would rather see that as a later enhancement once
we have the simple named parameters in place - my proposal does not
conflict with having this as a later feature.
I don't think you can have this as a later feature. It needs to be designed in 
from the first version.

The mangling of "void bar(int)" is
	_Z3bari
	?bar@@YAXH@Z

If this is used for "void bar(int :a)", then you CANNOT later overload with 
"void bar(int :x)". In other words, for named arguments to be overloadable at 
any point in the future, they must have been overloadable at all points in the 
past too.


I think that these are orthogonal features and should be defined independently.

For me named parameters are just syntactic sugar and shouldn't change neither the signature of the function nor the mangling.

For me, having two declarations of the same function with different parameters, should just allow to use those parameters as named parameters.

So having

    void f(int a, int b);

    void f(int x, int y);

would allow to use the named parameters and b and x and y respectively.

The question is why allowing that?

The idea is that we want to avoid a successful TU to be modified because we add an additional declaration on one of the header files.


As this would request to compiler to store somewhere all the declarations of a specific function, I believe that in order to don't pay for what we don't use, we would need to opt in for the feature is some specific way, that I don't worry about. E.g.

    void f(a:int, b:int);

    void f(x:int x, y:int);

Only in this case the named parameters should be allowed.

I could live also having the naming by default, but I believe that the name used for something that today is not in the public interface would very often be not the most appropriated hence the need to opt in.

We should note that if opt in is not retained, this doesn't mean that from the standard library point of view we shouldn't opt in to have a public parameter name, as it shall be part of the wording, so bikesheing will not be avoided.


The other alternatives for me are

* names are part of the signature and we can overload on them. This is something else for me (this would be syntactic sugar for tag dispatching). I'm not saying we don't need something like that, just that I don't want to name this or mix with  name parameters as it is naming an overload not a parameter.

    The syntax for this kind of additional named function/constructor overload could be different as the implications are different and maybe the needs are also different.

    If we need something else to be able to oveload it should be something that is on the domain of types and so syntactic sugar for tag types. This will change the signature and of course the mangling of the function.


* only one set of names is possible for all the TU (this need the kind of mangling you are suggesting). I can live without this. IIRC this was the approach that was rejected by the committee.

* only one set of names is possible for a TU. This is easy to check but I it is not stable. Adding an additional header file would add additional forward declaration that could break the code.


Vicente




Thiago Macieira

unread,
Aug 17, 2018, 11:05:21 AM8/17/18
to Vicente J. Botet Escriba, std-pr...@isocpp.org
On Friday, 17 August 2018 07:46:42 PDT Vicente J. Botet Escriba wrote:
> As this would request to compiler to store somewhere all the
> declarations of a specific function, I believe that in order to don't
> pay for what we don't use, we would need to opt in for the feature is
> some specific way, that I don't worry about. E.g.
>
> void f(a:int, b:int);
>
> void f(x:int x, y:int);
>
> Only in this case the named parameters should be allowed.

Agreed. To me, parameter names need to be opt-in. Anything else is simply not
workable as the names have not and will continue not to be stable, so they
aren't usable by users in the first place.

But my question to you is: are those two function declarations the same
function? If they are, how do you propose we declare them so that they are
*not* the same function?

> The syntax for this kind of additional named function/constructor
> overload could be different as the implications are different and maybe
> the needs are also different.

Matthew Woehlke

unread,
Aug 17, 2018, 11:16:21 AM8/17/18
to std-pr...@isocpp.org, David Brown
On 2018-08-17 06:14, David Brown wrote:
> On 16/08/18 15:08, mihailn...@gmail.com wrote:
>> There are tree major problems with such simple approaches
>>
>> First - you make something, that has been private for 30+ years, public.
>
> No, it is not. The names for parameters are in function declarations,
> in headers - these are public.

No, they aren't. Or, more correctly, while they are "public" in the
sense that a user can see them by reading the header, they aren't a
(normative) part of the API, any more than comments are. To *the
compiler*, they may as well not exist.

You are proposing to change that. That is a *serious* change.

> But I would want to be able to call existing functions using named
> arguments, and I would want to call new functions with positional
> arguments. I am sceptical to the idea of being able to force the use of
> named arguments at any point

I don't think anyone has proposed that, with one exception:

foo(int :a, int :b = 3, int :c = 12);
foo(1, c: 6); // name *must* be used here because 'b' is omitted

At least, I don't know any reason why we would need to require names to
be used, other than the above example.

> and the whole idea would be useless if
> special syntax were needed to declare that a function uses named parameters.

I don't think you can *avoid* that, since there is very strong
opposition (see above) to turning all existing functions into functions
that have named parameters. Also, everyone that *is* proposing to have
named parameters with syntax apparently don't share this opinion.

--
Matthew

Justin Bassett

unread,
Aug 17, 2018, 11:17:47 AM8/17/18
to std-pr...@isocpp.org


On Fri, Aug 17, 2018, 7:46 AM Vicente J. Botet Escriba <vicent...@wanadoo.fr> wrote:
* only one set of names is possible for all the TU (this need the kind of mangling you are suggesting). I can live without this. IIRC this was the approach that was rejected by the committee.

Is there a way I can see the comments the committee has made on a proposal? My impression from my research was counter to this: I thought the committee didn't like that names could vary across translation units. I would like to read the comments on proposals, rather than just guess what the committee's concerns are

Matthew Woehlke

unread,
Aug 17, 2018, 11:26:02 AM8/17/18
to std-pr...@isocpp.org
On 2018-08-17 08:36, Jake Arkinstall wrote:
> At least python handles the overloading issue - by not supporting
> overloading. But that also comes with its own problems - it encourages
> ridiculous interfaces. See pandas.to_csv as a good example - credit to them
> for making such an incredible library, but their many-optional-parameter
> interfaces combined with their inconsistent parameter naming styles results
> in a usability nightmare, and we're opening ourselves up to that mentality
> with the very mention of named parameters.

A Python developer would say, "thank God we don't have those awful C++
interfaces with their dozens of confusing and redundant overloads". And
there is truth to that. I've written such interfaces, and there are
absolutely times I would *much* rather have a Pythonic interface with a
great big bag of *named* parameters.

Happiness, I think, lies somewhere between the two extremes, and/or in
being able to choose the most appropriate approach for any given problem
(i.e. having both tools in our toolbox).

--
Matthew

Jake Arkinstall

unread,
Aug 17, 2018, 12:18:08 PM8/17/18
to std-pr...@isocpp.org


On Fri, 17 Aug 2018, 16:16 Matthew Woehlke, <mwoehlk...@gmail.com> wrote:

  foo(int :a, int :b = 3, int :c = 12);
  foo(1, c: 6); // name *must* be used here because 'b' is omitted

Ah. That does demolish my idea about using strong types as named wrappers for the underlying type. Well, it makes it insufficient, at least - in order to make it work we'd need to change the language to keep trying parameter ordering until the types fit.

Unlike an approach that changes mangling directly, this does mean that the following non-named code would become valid:

foo(const int& a=1, const std::string& b="hello");
foo("world"); //yuck

Which, innocent enough in this example, is going to be a source of bugs when an implicit conversion exists - unless that ordering search matches act as explicit, which is going to cause its own problems in terms of usability.

Matthew Woehlke

unread,
Aug 17, 2018, 12:54:17 PM8/17/18
to std-pr...@isocpp.org, Justin Bassett
On 2018-08-17 00:04, Justin Bassett wrote:
> I agree for the most part, but I disagree that this should be a valid
> overload set, unless parameters are name-only:
>
> void bar(int :a);
> void bar(int :x);
>
> The problem is that bar(4) is ambiguous.

So what?

void foo(int, double=1.);
void foo(int, int=2);

foo(1); // error: ambiguous

void bar(int :a); // #1
void bar(int :x); // #2

bar(1); // error: ambiguous
bar(.z=1); // error: no matching declaration
bar(.a=1); // okay, calls #1

I don't see a problem here.

> Overloading on name-only parameters is just fine.

What is a "name only" parameter?

--
Matthew

Matthew Woehlke

unread,
Aug 17, 2018, 1:00:05 PM8/17/18
to std-pr...@isocpp.org, Jake Arkinstall
On 2018-08-17 12:17, Jake Arkinstall wrote:
> On Fri, 17 Aug 2018, 16:16 Matthew Woehlke wrote:
>> foo(int :a, int :b = 3, int :c = 12);
>> foo(1, c: 6); // name *must* be used here because 'b' is omitted
>
> Ah. That does demolish my idea about using strong types as named wrappers
> for the underlying type.

Well... yes and no. "Strong types as named wrappers for the underlying
type" sounds suspiciously like where my own thoughts on this matter have
been going. However, while I would envision a library/type component, we
do *need* language-level support... not just for syntactic sugar, but
for this, specifically. (And since this is my #1 "killer feature" for
named arguments... don't get me wrong, tagged dispatch / named overload
is #2, but skipping arguments is #1 :-).)

> Well, it makes it insufficient, at least - in order to make it work
> we'd need to change the language to keep trying parameter ordering
> until the types fit.
>
> Unlike an approach that changes mangling directly, this does mean that the
> following non-named code would become valid:
>
> foo(const int& a=1, const std::string& b="hello");
> foo("world"); //yuck
>
> Which, innocent enough in this example, is going to be a source of bugs
> when an implicit conversion exists - unless that ordering search matches
> act as explicit, which is going to cause its own problems in terms of
> usability.

I would not allow skipping for unnamed parameters.

--
Matthew

David Brown

unread,
Aug 17, 2018, 1:12:20 PM8/17/18
to std-pr...@isocpp.org
On 17/08/18 17:16, Matthew Woehlke wrote:
> On 2018-08-17 06:14, David Brown wrote:
>> On 16/08/18 15:08, mihailn...@gmail.com wrote:
>>> There are tree major problems with such simple approaches
>>>
>>> First - you make something, that has been private for 30+ years, public.
>>
>> No, it is not. The names for parameters are in function declarations,
>> in headers - these are public.
>
> No, they aren't. Or, more correctly, while they are "public" in the
> sense that a user can see them by reading the header, they aren't a
> (normative) part of the API, any more than comments are. To *the
> compiler*, they may as well not exist.
>
> You are proposing to change that. That is a *serious* change.
>

Nah, you are overreacting. At most, it is a /tiny/ issue compared to
the huge amount of implementation stuff in typical C++ headers that
really is supposed to be private implementation details. (Hint: lots of
it is labelled "private:".)

It is very common to give sensible names to parameters in headers - just
as it is common to write sensible comments in headers. Yes, the
compiler ignores them - but humans reading the headers do not, and lots
of other tools do /not/ ignore either parameter names or even comments.
IDE's regularly show parameter names in some sort of tooltip or hint
when you are typing a function call, or will show them as a sort of
function summary - possibly along with nearby comments. Documentation
tools like doxygen use them.

If you want to make it hard for people to use functions by obscuring the
point of the parameters, leave them unnamed. Don't put a name in the
header and pretend it's a secret.

>> But I would want to be able to call existing functions using named
>> arguments, and I would want to call new functions with positional
>> arguments. I am sceptical to the idea of being able to force the use of
>> named arguments at any point
>
> I don't think anyone has proposed that, with one exception:
>
> foo(int :a, int :b = 3, int :c = 12);
> foo(1, c: 6); // name *must* be used here because 'b' is omitted
>
> At least, I don't know any reason why we would need to require names to
> be used, other than the above example.

Some people have suggested a syntax for function declarations (or
definitions) that indicated that one or more of the parameters /must/ be
used as named parameters. I can see that being useful if they are to be
used for overloads, but not otherwise.


>
>> and the whole idea would be useless if
>> special syntax were needed to declare that a function uses named parameters.
>
> I don't think you can *avoid* that, since there is very strong
> opposition (see above) to turning all existing functions into functions
> that have named parameters. Also, everyone that *is* proposing to have
> named parameters with syntax apparently don't share this opinion.
>

I don't think your argument against allowing people to use named
parameters for existing functions is at all convincing. I am happy to
accept that /you/ have strong opinions against it - and therefore that
there is /some/ opposition to it. Whether a lot of people share your
opinion here or not, I don't know. All we can be sure about for now is
that some people strongly approve of it, some people strongly oppose it.
I guess that's why we have a C++ standards committee, and not a C++
standards dictatorship.



mihailn...@gmail.com

unread,
Aug 17, 2018, 1:19:40 PM8/17/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
It is not optional, but the help writing them is.
So ok, weak names are helpers writing correctly the contract, not the contract itself. They are like a clerk - you can ask him for help, you can ask him to verify OR you can do it yourself if you know what you are doing. 

  

Or to put it another way, the only reason "override" is optional is that it would be a huge compatibility break to make it mandatory. That feature is very similar to here: both cases are stating the meaning of something explicitly.

Why is this meaning important enough to be said and verified sometimes and not other-times?

Lastly, a big problem I have with your conception of weak names is that you're trying to make them a full-fledged piece of information connected with a value (in order to make forwarding work)... but you're not using any existing C++ mechanism to provide this channel of information. Names aren't types. They aren't values. They're associated with parameter names, but they have to be able to pass through `std::forward` somehow. And users have to be able to pass them to non-named functions (like loggers) as well as named ones.


Agreed, for better or worse names are outside the language as defined use today. Probably the closest are attributes. Super pretty attributes. We just need a way to mark (or hardcode) std::forward as forwarding them

And BTW, it is not some my conception - C# uses weak names for 10 years now, pure compiler syntax, outside of types or function signature.

Also, it should be noted it is the weak ones that were proposed for C++ for 26 years now. People just seem to need some extra level of (trivial to get) protection and explicitness. 

 
The various strong naming proposals make these names actual parts of the C++ language. Some make them into values passed into the function. Others make the function take an unnamed struct. And so forth.

Weak names can't work through normal C++ channels of information.

Can this be solved by types - only if workaround is solving 
Can this be solved by strong names - well, yeah, at the price of thinking the users are idiots having them to repeat like monkeys every name that "we think it is important".

To the degree that I think named parameters are important enough to have in the language, I will happily pay the price of the latter if it means not having to pay the price of having two syntaxes for things that are essentially the same thing.

They are not the same, and languages that have only one syntax pick one or another - C# weak, Objective C- strong.

I am not proposing something concretely, just saying both are valuable, and theoretically we could have both.

As said, the only reason I would prefer to get weak first, is because the others are easier to workaround.

And I do believe having mandatory only names will be very annoying - after all you would need at most one or two strongs to disambiguate and all the others will be a pain.
Or, we would need extra syntax to make some names optional (ala Swift), but then, well, we have both strong and weak syntaxes, don't we?

rect(center c: Point, _ br: Point) //< Swift "two syntaxes" mandatory and optional

Nicol Bolas

unread,
Aug 17, 2018, 1:30:46 PM8/17/18
to ISO C++ Standard - Future Proposals


On Friday, August 17, 2018 at 1:12:20 PM UTC-4, David Brown wrote:
On 17/08/18 17:16, Matthew Woehlke wrote:
> On 2018-08-17 06:14, David Brown wrote:
>> On 16/08/18 15:08, mihailn...@gmail.com wrote:
>>> There are tree major problems with such simple approaches
>>>
>>> First - you make something, that has been private for 30+ years, public.
>>
>> No, it is not.  The names for parameters are in function declarations,
>> in headers - these are public.
>
> No, they aren't. Or, more correctly, while they are "public" in the
> sense that a user can see them by reading the header, they aren't a
> (normative) part of the API, any more than comments are. To *the
> compiler*, they may as well not exist.
>
> You are proposing to change that. That is a *serious* change.
>

Nah, you are overreacting.  At most, it is a /tiny/ issue compared to
the huge amount of implementation stuff in typical C++ headers that
really is supposed to be private implementation details.  (Hint: lots of
it is labelled "private:".)

"Overreacting"? Show me a standard library implementation that actually uses the parameter names that the standard library defines. What's more, standard libraries cannot use those parameter names. Why?

Macro defensiveness.

Users can define macros with almost any names. And standard library headers are required to not be affected by such macros. As such, standard library implementations are required to use the set of names that macros cannot touch: names that are restricted for the implementation.

So if you can't even rely on the standard library's parameter names, you would be unable to write cross-platform code that uses those names. And if you can't even rely on the standard library... what can you rely on?

Ville Voutilainen

unread,
Aug 17, 2018, 1:31:45 PM8/17/18
to ISO C++ Standard - Future Proposals
On 17 August 2018 at 20:30, Nicol Bolas <jmck...@gmail.com> wrote:
> "Overreacting"? Show me a standard library implementation that actually uses
> the parameter names that the standard library defines. What's more, standard
> libraries cannot use those parameter names. Why?
>
> Macro defensiveness.
>
> Users can define macros with almost any names. And standard library headers
> are required to not be affected by such macros. As such, standard library
> implementations are required to use the set of names that macros cannot
> touch: names that are restricted for the implementation.
>
> So if you can't even rely on the standard library's parameter names, you
> would be unable to write cross-platform code that uses those names. And if
> you can't even rely on the standard library... what can you rely on?

This problem ceases to be a problem once modules land.

Nicol Bolas

unread,
Aug 17, 2018, 1:37:34 PM8/17/18
to ISO C++ Standard - Future Proposals
And of course, `#include`s of C++ standard library headers are not required to actually "include" text files (which is why they don't end in `.h`). They can just be `import` directives under the hood. Or they can include text files that themselves just import a module.

That bears thinking about. But at the same time, if we go this route, we effectively enforce standard libraries to use those names for its parameters. Names become a de-facto part of their interfaces. That will make it difficult for standard library implementations to compile on both C++17 and C++20/whatever-has-modules.

Ville Voutilainen

unread,
Aug 17, 2018, 1:49:19 PM8/17/18
to ISO C++ Standard - Future Proposals
On 17 August 2018 at 20:37, Nicol Bolas <jmck...@gmail.com> wrote:
>> > So if you can't even rely on the standard library's parameter names, you
>> > would be unable to write cross-platform code that uses those names. And
>> > if
>> > you can't even rely on the standard library... what can you rely on?
>>
>> This problem ceases to be a problem once modules land.
>
>
> And of course, `#include`s of C++ standard library headers are not required
> to actually "include" text files (which is why they don't end in `.h`). They
> can just be `import` directives under the hood. Or they can include text
> files that themselves just import a module.
>
> That bears thinking about. But at the same time, if we go this route, we
> effectively enforce standard libraries to use those names for its
> parameters. Names become a de-facto part of their interfaces. That will make
> it difficult for standard library implementations to compile on both C++17
> and C++20/whatever-has-modules.

It's worth noting that some languages like Common Lisp do standardize
the names of some
parameters of the standard library functions. So it's not
unfathomable; prior to modules,
the problem was that we _couldn't_ standardize the names, it was just
not possible.

What I have established, having had discussions about named arguments
on multiple occasions,
is that the following pain points are important:

1) it needs to be an opt-in for the author of a library, so no
automagic naming. Some library
authors do not want users relying on names that the library author
didn't expect them to rely
on, in order to avoid breakage that wasn't considered beforehand.

2) The standard library part is the other major one; it's a bit
embarrassing if the facility can't work
with any standard library functions, and prior to modules, it indeed can't.

There have been various suggestions to make names part of the ABI and
part of the function type,
so that the function can't be called without named arguments, in the
traditional ordered-unnamed
arguments.
That makes wrapping such functions in binds and lambdas and whatnot
horrible. Lisp, for example,
still allows ordered-unnamed calls even if some parameters are
declared &key. Making it part
of the ABI is likewise nice for some things (changing the names breaks
ABI, silent change of
meaning doesn't happen for users), but it's unfortunate from other
perspectives, like having
to invent new manglings and again running into problems with
ordered-unnamed calls.

I do expect further discussion and proposals for named arguments. Some
committee members
are trying fairly hard to get named arguments in.

Matthew Woehlke

unread,
Aug 17, 2018, 1:53:14 PM8/17/18
to std-pr...@isocpp.org, David Brown
On 2018-08-17 13:12, David Brown wrote:
> On 17/08/18 17:16, Matthew Woehlke wrote:
>> On 2018-08-17 06:14, David Brown wrote:
>>> On 16/08/18 15:08, mihailn...@gmail.com wrote:
>>>> First - you make something, that has been private for 30+ years, public.
>>>
>>> No, it is not. The names for parameters are in function declarations,
>>> in headers - these are public.
>>
>> No, they aren't. Or, more correctly, while they are "public" in the
>> sense that a user can see them by reading the header, they aren't a
>> (normative) part of the API, any more than comments are. To *the
>> compiler*, they may as well not exist.
>>
>> You are proposing to change that. That is a *serious* change.
>
> Nah, you are overreacting.

You are certainly entitled to believe that. I happen to disagree, but
more importantly, AFAICT quite a few others also disagree. The point is,
you are facing an uphill battle to convince the committee on this matter.

Right now, changing the names of function parameters is SC+BC. IIUC, you
are proposing to make it at least SIC, if not also BIC. I honestly can't
see how you're going to convince the committee that that's okay.

> At most, it is a /tiny/ issue compared to
> the huge amount of implementation stuff in typical C++ headers that
> really is supposed to be private implementation details. (Hint: lots of
> it is labelled "private:".)

You mean like this?

class foo
{
...interface...
private:
FooPrivate* d;
};

No so much "leaked" there...

> Some people have suggested a syntax for function declarations (or
> definitions) that indicated that one or more of the parameters /must/ be
> used as named parameters. I can see that being useful if they are to be
> used for overloads, but not otherwise.

Well, I don't believe I've ever been one of them. I currently don't see
any reason why such a feature would be desirable.

There *are* instances (overload resolution, skipping parameters) when a
*call* needs to use a name, but I don't see the value in having a
declaration that says you *must always* use the name.

>>> and the whole idea would be useless if
>>> special syntax were needed to declare that a function uses named parameters.
>>
>> I don't think you can *avoid* that, since there is very strong
>> opposition (see above) to turning all existing functions into functions
>> that have named parameters. Also, everyone that *is* proposing to have
>> named parameters with syntax apparently don't share this opinion.
>
> I don't think your argument against allowing people to use named
> parameters for existing functions is at all convincing.

Who's arguing that?

I would make this perfectly legal:

void foo(int x); // note: not a named parameter
foo(.x=5); // okay, but maybe warn in pedantic mode
foo(.a=5); // okay, but probably warn

--
Matthew

Jake Arkinstall

unread,
Aug 17, 2018, 2:08:55 PM8/17/18
to std-pr...@isocpp.org
On Fri, Aug 17, 2018 at 5:54 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
On 2018-08-17 00:04, Justin Bassett wrote:
> I agree for the most part, but I disagree that this should be a valid
> overload set, unless parameters are name-only:
>
> void bar(int :a);
> void bar(int :x);
>
> The problem is that bar(4) is ambiguous.

So what?

  void foo(int, double=1.);
  void foo(int, int=2);

  foo(1); // error: ambiguous

TRWTF is the fact that the second function definition is even allowed in the first place - IMO it should be (and have always been) a 'redefinition' error. But I do see your point.

> Overloading on name-only parameters is just fine.

What is a "name only" parameter?

I imagine it's a parameter that you can *only* provide by specifying the name.


On Fri, Aug 17, 2018 at 6:00 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
On 2018-08-17 12:17, Jake Arkinstall wrote:
> On Fri, 17 Aug 2018, 16:16 Matthew Woehlke wrote:
>>   foo(int :a, int :b = 3, int :c = 12);
>>   foo(1, c: 6); // name *must* be used here because 'b' is omitted
>
> Ah. That does demolish my idea about using strong types as named wrappers
> for the underlying type.

Well... yes and no. "Strong types as named wrappers for the underlying
type" sounds suspiciously like where my own thoughts on this matter have
been going. However, while I would envision a library/type component, we
do *need* language-level support... not just for syntactic sugar, but
for this, specifically. (And since this is my #1 "killer feature" for
named arguments... don't get me wrong, tagged dispatch / named overload
is #2, but skipping arguments is #1 :-).)

That's where we differ. Only because skipping arguments tends to lead to sloppy interfaces (but I believe we agree to disagree on this - it is a matter of personal preference).


> Well, it makes it insufficient, at least - in order to make it work
> we'd need to change the language to keep trying parameter ordering
> until the types fit.
>
> Unlike an approach that changes mangling directly, this does mean that the
> following non-named code would become valid:
>
> foo(const int& a=1, const std::string& b="hello");
> foo("world"); //yuck
>
> Which, innocent enough in this example, is going to be a source of bugs
> when an implicit conversion exists - unless that ordering search matches
> act as explicit, which is going to cause its own problems in terms of
> usability.

I would not allow skipping for unnamed parameters.

That's the catch, though.

I suggested using a strong type mechanism for named parameters as a high-level solution. It just requires generating a struct from the named parameter at the declaration site, and reinterpreting the input as that struct when named through the call site (reinterpreting it as the expected type before entering the function itself). This can be done relatively early in translation (or even by running code through a few lines of perl before compilation). As such, it doesn't require changing that much in the language (in particular, mangling and selecting the right call at the call site - that's handled indirectly just by introducing the types).

But this high-level solution doesn't suffice if we also want to have a special case (parameter skipping) - it would be more consistent to just make it apply to all function calls (which makes me nervous). The only way I can imagine it COULD work using this approach, but allowing skipping only for these calls, is if we generate the 2^N possible function calls (N: number of arguments with defaults) as unique symbols.

If we want to make the parameters rearrangeable too (which would make sense if we really want to get pythonic bag-functions), all (sum_{k=0}^{N} N!/k!) possible combinations. That's... horrifying. At least if we enable the parameter skipping for ordinary functions too, we can get away with N! symbols (scary, albeit less so than (sum_{k=0}^{N} N!/k!)).

If we allow skipping AND reordering of function arguments for normal (un-named) calls, such that:

f(S=a, T b=0, U c=0);
f(U{},S{}) // okay

Then we'd be back to the status quo with a single symbol. But obviously, that would make the language completely unstable.


So unless I'm missing something (which is likely, this stuff isn't my forte), allowing skipping (and/or reordering) just for these will require a language change at a later translation phase. And in a later translation phase, the strong type approach seems rather pointless - it could just be done directly by the compiler, however the compiler wants to do it.

mihailn...@gmail.com

unread,
Aug 17, 2018, 2:41:10 PM8/17/18
to ISO C++ Standard - Future Proposals
Wouldn't the interface to a template still be its header? Aren't modules only for compiled code? 

How is the relation b/w modules and templates envisioned to be? 

David Brown

unread,
Aug 17, 2018, 2:42:21 PM8/17/18
to Matthew Woehlke, std-pr...@isocpp.org


On 17/08/18 19:53, Matthew Woehlke wrote:
> On 2018-08-17 13:12, David Brown wrote:
>> On 17/08/18 17:16, Matthew Woehlke wrote:
>>> On 2018-08-17 06:14, David Brown wrote:
>>>> On 16/08/18 15:08, mihailn...@gmail.com wrote:
>>>>> First - you make something, that has been private for 30+ years, public.
>>>>
>>>> No, it is not. The names for parameters are in function declarations,
>>>> in headers - these are public.
>>>
>>> No, they aren't. Or, more correctly, while they are "public" in the
>>> sense that a user can see them by reading the header, they aren't a
>>> (normative) part of the API, any more than comments are. To *the
>>> compiler*, they may as well not exist.
>>>
>>> You are proposing to change that. That is a *serious* change.
>>
>> Nah, you are overreacting.
>
> You are certainly entitled to believe that. I happen to disagree, but
> more importantly, AFAICT quite a few others also disagree. The point is,
> you are facing an uphill battle to convince the committee on this matter.

Fair enough. As I say, I feel differently - but final decisions here
will be up to the committee. I doubt if anything I might say here is
new to the committee - if they didn't like it before, they won't like it
now and I will not be able to convince them. I see C++ from my little
corner of the world, looking at what makes sense to me - they look at a
far, far wider view. They might not make the decision that I think is
best for /me/, but I'm sure they will make a considered judgement on
what they think is best overall.

And certainly if the choice is between named parameters for functions
declared specifically to allow named parameters, or no named parameters
at all, my vote would be with them. A second-best solution would be
better than no solution. (But see my final comment in this post - it
could be that I have been misunderstanding your point.)

>
> Right now, changing the names of function parameters is SC+BC. IIUC, you
> are proposing to make it at least SIC, if not also BIC. I honestly can't
> see how you're going to convince the committee that that's okay.

I'm sorry, I don't recognise the abbreviations SC, BC, SIC and BIC.

>
>> At most, it is a /tiny/ issue compared to
>> the huge amount of implementation stuff in typical C++ headers that
>> really is supposed to be private implementation details. (Hint: lots of
>> it is labelled "private:".)
>
> You mean like this?
>
> class foo
> {
> ...interface...
> private:
> FooPrivate* d;
> };
>
> No so much "leaked" there...
>

Well, no, not in a case like this. But when the private section holds
members and possibly inline implementations, it is leaked. This is
especially true of template libraries.

>> Some people have suggested a syntax for function declarations (or
>> definitions) that indicated that one or more of the parameters /must/ be
>> used as named parameters. I can see that being useful if they are to be
>> used for overloads, but not otherwise.
>
> Well, I don't believe I've ever been one of them. I currently don't see
> any reason why such a feature would be desirable.
>
> There *are* instances (overload resolution, skipping parameters) when a
> *call* needs to use a name, but I don't see the value in having a
> declaration that says you *must always* use the name.
>

Agreed.

>>>> and the whole idea would be useless if
>>>> special syntax were needed to declare that a function uses named parameters.
>>>
>>> I don't think you can *avoid* that, since there is very strong
>>> opposition (see above) to turning all existing functions into functions
>>> that have named parameters. Also, everyone that *is* proposing to have
>>> named parameters with syntax apparently don't share this opinion.
>>
>> I don't think your argument against allowing people to use named
>> parameters for existing functions is at all convincing.
>
> Who's arguing that?
>
> I would make this perfectly legal:
>
> void foo(int x); // note: not a named parameter
> foo(.x=5); // okay, but maybe warn in pedantic mode
> foo(.a=5); // okay, but probably warn
>

Do you mean you would be happy to allow the parameter name in an
existing declaration to be used in function calls as a check? But you
would presumably not allow such names to be used for overloads - that
would require a specific extra syntax in the declaration. Is that correct?

I would be entirely happy with such a situation - my main objective is
to aid in writing correct and readable code, and my compiler (given
appropriate flags) would accept "foo(.x = 5)" without complaint while
warning about "foo(.a = 5)", I'd be very satisfied.

I'd been even happier if it accepted this:

void foo(int x, int y);
foo(.y = 1, .x = 2); // Same as foo(2, 1)

But if that's a problem, I'd be fine without it.

Matthew Woehlke

unread,
Aug 17, 2018, 2:55:23 PM8/17/18
to std-pr...@isocpp.org, Jake Arkinstall
On 2018-08-17 14:08, Jake Arkinstall wrote:
> On Fri, Aug 17, 2018 at 5:54 PM, Matthew Woehlke wrote:
>> What is a "name only" parameter?
>
> I imagine it's a parameter that you can *only* provide by specifying the
> name.

I assume so also. I don't think we need those as such; not at the
declaration level, certainly.

There will be cases where *calling* a function will need a name, either
due to skipping params or to disambiguate overloads that differ only by
explicit parameter names. But I don't think we need a mechanism that
says 'we will not let you call this function unless you explicitly give
the name' (overload ambiguities aside).

> On Fri, Aug 17, 2018 at 6:00 PM, Matthew Woehlke wrote:
>> [...] skipping arguments is #1 :-).
>
> That's where we differ. Only because skipping arguments tends to lead to
> sloppy interfaces (but I believe we agree to disagree on this - it is a
> matter of personal preference).

They aren't ideal, but I've run into plenty of instances where I need a
function that has quite a few "knobs", of which the user will likely
never need all of them at any given call site, but *which* ones vary.

Right now the solution is to have a lot of overloads with the arguments
in different orders. This is ugly, a pain to maintain, and possibly
inefficient.

>> I would not allow skipping for unnamed parameters.
>
> That's the catch, though.
>
> I suggested using a strong type mechanism for named parameters as a
> high-level solution. It just requires generating a struct from the named
> parameter at the declaration site, and reinterpreting the input as that
> struct when named through the call site (reinterpreting it as the expected
> type before entering the function itself). This can be done relatively
> early in translation (or even by running code through a few lines of perl
> before compilation). As such, it doesn't require changing that much in the
> language (in particular, mangling and selecting the right call at the call
> site - that's handled indirectly just by introducing the types).
>
> But this high-level solution doesn't suffice if we also want to have a
> special case (parameter skipping) - it would be more consistent to just
> make it apply to all function calls (which makes me nervous). The only way
> I can imagine it COULD work using this approach, but allowing skipping only
> for these calls, is if we generate the 2^N possible function calls (N:
> number of arguments with defaults) as unique symbols.

Doesn't the struct approach with designated initializers take care of
this, though?

Anyway, my own current thoughts are not to use a struct, but to make
each named parameter something like `arg<int, "foo"_lit>`. Where we need
language support, then, is:

void foo(int, int .b=1, int .c=2);
foo(5, .c=7);

Here, the compiler needs to know that, as soon as it sees a named
argument, it should consider all calls that match the argument list up
to that point *and* at some following point have an argument named `c`.
(And have defaulted arguments for whatever doesn't match up.)

IOW, what an implementation would do is:

- Start with all possible overloads of `foo`.
- Throw out any that can't match `foo(int, ...)`
- Look for one that has a named argument (with ordinal > 0) named `c`.
- Repeat for any more named arguments.
- Throw out anything that's left for which any "unbound" arguments do
not have defaults.
- Emit a call to the resulting function (if exactly one) or issue a
diagnostic.

At no point do we actually generate additional function prototypes.

> So unless I'm missing something (which is likely, this stuff isn't my
> forte), allowing skipping (and/or reordering) just for these will require a
> language change at a later translation phase. And in a later translation
> phase, the strong type approach seems rather pointless - it could just be
> done directly by the compiler, however the compiler wants to do it.

Ah, yes, I think I see. There is no *practical* reason why the logic I
outline above couldn't be applied to unnamed arguments also. That's more
a matter of "for sanity".

The main use for strong typing, at least in my current vision, is for
forwarding. (And maybe mangling.) In that sense, I think we agree.

For example, I can do this:

void bar(int .x); // #1
void bar(int .y); // #2

template <typename Args...>
void foo(Args... args)
{
bar(args...);
}

// okay, instantiates foo<arg<int, "y"_lit>> which calls #2
foo(.y=1);

--
Matthew

Cleiton Santoia

unread,
Aug 17, 2018, 2:56:35 PM8/17/18
to ISO C++ Standard - Future Proposals
Some time ago I "proposed" something about this in other std thread.

My idea was just change 2 things in lambdas:

1 - Allow the lambda members to be public;
2 - Allow the '{}' to be optional in lambda definition;

So this will be a valid:
template < RectLeftTop Rect > // Concept RectLeftTop
void draw_rect(const Rect& r){
   
... // this uses r.left and r.top
}

template < RectX1Y1 Rect // Concept RectX1Y1
void draw_rect(const Rect& r){
   
... // this overload uses r.x1 and r.y1
}

void test() {
 
auto x = 10;
  auto lambda = [top=x, left=100, width=x+30, height=100]; // lambda declaration without '{}'
  lambda.top = lambda.top * 2;   // using public members from lambda
 
draw_rect(lambda);
  draw_rect([x1=x, x2=x+90, y1=30, y2=100]);  // Use directly a lambda
}

Matthew Woehlke

unread,
Aug 17, 2018, 3:11:48 PM8/17/18
to std-pr...@isocpp.org, David Brown
On 2018-08-17 14:42, David Brown wrote:
> On 17/08/18 19:53, Matthew Woehlke wrote:
>> Right now, changing the names of function parameters is SC+BC. IIUC, you
>> are proposing to make it at least SIC, if not also BIC. I honestly can't
>> see how you're going to convince the committee that that's okay.
>
> I'm sorry, I don't recognise the abbreviations SC, BC, SIC and BIC.

S/B = source (API) / binary (ABI)
C/IC = [in]compatible

IOW, right now if I release a new version of my library that changes a
parameter name, it has no effect. Under your proposal... does my
previously compiled application (which uses said library) still run
(BC)? Can I recompile it without needing to change its sources (SC)?

>>    class foo
>>    {
>>      ...interface...
>>    private:
>>      FooPrivate* d;
>>    };
>>
>> No so much "leaked" there...
>
> Well, no, not in a case like this.  But when the private section holds
> members and possibly inline implementations, it is leaked.  This is
> especially true of template libraries.

True. The point is that "some libraries are designed poorly" is not an
excuse to leak even *more*.

>> On 2018-08-17 13:12, David Brown wrote:
>>> I don't think your argument against allowing people to use named
>>> parameters for existing functions is at all convincing.
>>
>> Who's arguing that?
>>
>> I would make this perfectly legal:
>>
>>    void foo(int x); // note: not a named parameter
>>    foo(.x=5); // okay, but maybe warn in pedantic mode
>>    foo(.a=5); // okay, but probably warn
>
> Do you mean you would be happy to allow the parameter name in an
> existing declaration to be used in function calls as a check?

If I make a call with a named parameter that resolves to a function that
does not have explicitly named parameters, then yes, I'm fine with the
compiler *warning* me if the names don't match.

> But you would presumably not allow such names to be used for
> overloads - that would require a specific extra syntax in the
> declaration. Is that correct?
Right.

I guess in that sense what I'm proposing is opt-out weak naming (that
only involves a possible compiler warning if you mismatch names) with
opt-in strong naming, where the "fun stuff" (argument skipping, and per
your question, name-based overloading) requires 'strong' naming.

The only way to opt out would be by not naming your parameters *at all*.
However, I guess if you wrote:

void foo(int x, int y);
void foo(int a, int b);

...then *either* `foo(.a=1)` or `foo(.x=1)` should be accepted with no
warning, and would call the same function. But this is all QoI stuff
that doesn't need to be part of the standard.

> I would be entirely happy with such a situation - my main objective is
> to aid in writing correct and readable code, and my compiler (given
> appropriate flags) would accept "foo(.x = 5)" without complaint while
> warning about "foo(.a = 5)", I'd be very satisfied.
>
> I'd been even happier if it accepted this:
>
>     void foo(int x, int y);
>     foo(.y = 1, .x = 2);    // Same as foo(2, 1)
>
> But if that's a problem, I'd be fine without it.

Well, it would *accept* that... with a warning... but it would be the
same as `foo(1, 2)` :-). If you wrote instead:

void foo(int .x, int .y);

...then it would either do what you meant, or be a hard error. I'm on
the fence whether reordering should be allowed. (And by "on the fence" I
mean a) *I* don't feel strongly either way, and b) I think there are
strong arguments for either case.)

--
Matthew

Ville Voutilainen

unread,
Aug 17, 2018, 3:21:29 PM8/17/18
to ISO C++ Standard - Future Proposals
On 17 August 2018 at 21:41, <mihailn...@gmail.com> wrote:
> Wouldn't the interface to a template still be its header? Aren't modules
> only for compiled code?

What "header"? You can put templates into a module just fine.

> How is the relation b/w modules and templates envisioned to be?

It would need to behave so that the names of parameters are handled in
the context
of the declaration site, not at the context of the instantiation site.

Justin Bassett

unread,
Aug 17, 2018, 11:37:19 PM8/17/18
to std-pr...@isocpp.org
What I've gathered from this discussion is that we want the simplest form of named parameters that can make it into the standard while not prohibiting a future proposal for some of the more niche features. After deliberating on this, I arrived at something quite similar to P0671R0 . Thus, the most important question that needs answering is: what were the committee's concerns about P0671R0? What were the reasons behind the changes made in R1 and R2? If we cannot address the concerns the committee had, there's no reason to propose a similar proposal. Without knowing the concerns, it's impossible to address them.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUb_iJ8B%3DELuJgBVo-R3fvPPOcgjMobr_4hvEQSR2%3DoALQ%40mail.gmail.com.

Vicente J. Botet Escriba

unread,
Aug 18, 2018, 4:08:45 AM8/18/18
to std-pr...@isocpp.org, Justin Bassett
I don't know when this was reviewed. Maybe you can contact the author directly.

Vicente

mihailn...@gmail.com

unread,
Aug 18, 2018, 5:26:57 AM8/18/18
to ISO C++ Standard - Future Proposals
 

On Saturday, August 18, 2018 at 6:37:19 AM UTC+3, Justin Bassett wrote:
What I've gathered from this discussion is that we want the simplest form of named parameters that can make it into the standard while not prohibiting a future proposal for some of the more niche features. After deliberating on this, I arrived at something quite similar to P0671R0 . Thus, the most important question that needs answering is: what were the committee's concerns about P0671R0? What were the reasons behind the changes made in R1 and R2? If we cannot address the concerns the committee had, there's no reason to propose a similar proposal. Without knowing the concerns, it's impossible to address them.

Here it is -> "introduce a new kind of functions". That is the reason. 

Also, this obviously does not address the named arguments, that are optional, the "weak" ones, so this solution will not make everyone happy. 


The only solution that covers all use cases is the one that has both types, much like Objective C (strong only) moved to Swift (strong, but some can be marked optional).  

We can argue about the syntax or if the name can be different from the argument, but we should accept, one-sided solution will not work:
 - Strong-only will be infuriating and hated for every argument named, that is not used for overload disambiguation, even now I hate Objective C/Swift error:error calls, and for many, many people 
   it is not what they expect. 
 - Weak-only will be seen as "unneeded" by some andin the same time, not doing their "primary" job - to disambiguate. 

We could probably introduce these in stages, though, as it is inevitable to have a "different syntax", much like swift has to have "different syntax" to mark ignorable ones.

Ville Voutilainen

unread,
Aug 18, 2018, 7:05:13 AM8/18/18
to ISO C++ Standard - Future Proposals
On 18 August 2018 at 06:37, Justin Bassett <jbass...@gmail.com> wrote:
> What I've gathered from this discussion is that we want the simplest form of
> named parameters that can make it into the standard while not prohibiting a
> future proposal for some of the more niche features. After deliberating on
> this, I arrived at something quite similar to P0671R0 . Thus, the most
> important question that needs answering is: what were the committee's
> concerns about P0671R0? What were the reasons behind the changes made in R1
> and R2? If we cannot address the concerns the committee had, there's no
> reason to propose a similar proposal. Without knowing the concerns, it's
> impossible to address them.

That proposal hasn't been discussed yet.

mihailn...@gmail.com

unread,
Aug 18, 2018, 7:31:33 AM8/18/18
to ISO C++ Standard - Future Proposals
Do you mean the later iterations? Because the original was discouraged, according to the trip report. 

Ville Voutilainen

unread,
Aug 18, 2018, 7:55:48 AM8/18/18
to ISO C++ Standard - Future Proposals
The R2 version is a different approach. In its preamble, it says
"This is different and I appreciate that you make up your mind based
on what's written here. I am almost certain: you haven't seen this
before."

mihailn...@gmail.com

unread,
Aug 18, 2018, 8:06:48 AM8/18/18
to ISO C++ Standard - Future Proposals
Yeah, but the Basett was asking about the original, because his idea is the same as R0
and he wanted to know what made the committee reject it.   

Ville Voutilainen

unread,
Aug 18, 2018, 8:15:13 AM8/18/18
to ISO C++ Standard - Future Proposals
On 18 August 2018 at 15:06, <mihailn...@gmail.com> wrote:
>> >> That proposal hasn't been discussed yet.
>> >
>> >
>> > Do you mean the later iterations? Because the original was discouraged,
>> > according to the trip report.
>>
>
>>
>> The R2 version is a different approach. In its preamble, it says
>> "This is different and I appreciate that you make up your mind based
>> on what's written here. I am almost certain: you haven't seen this
>> before."
>
>
> Yeah, but the Basett was asking about the original, because his idea is the
> same as R0
> and he wanted to know what made the committee reject it.

I stand corrected. So, looking at the discussion, the R0 version was
so that you couldn't call the functions as usual, with
ordered-unnamed,
and it suggested that the names need to be baked into the ABI. But
even with that removed,
the proposal still didn't pass. There were suggestions that a "fluent
API" (aka a parameter
type that has separate setters for different arguments) should just be
used instead (to which
I commented that that approach was invented 30 years ago and users
refuse to use it). A parameter
struct and use of designated initializers was suggested as another
work-around. Based on the
discussion notes, it seems that Evolution was still unconvinced that
this language feature
is necessary, given the work-arounds. That has been the fate of almost
every one of such
proposals.

If someone wants to work on this, contact Axel. I summarized the
situation at the end of the discussion
on P0671R0 with "Proposals on this will come back. We will not be able
to kill the desire. "

mihailn...@gmail.com

unread,
Aug 18, 2018, 10:54:39 AM8/18/18
to ISO C++ Standard - Future Proposals
Last few days made it clear where the problem is - there is no such thing like "named arguments" as self-explanatory topic.
Different people expect both different semantics and different implementations.

This creates noise, and murks the goal - what is the problem need fixing. 

If the community stands behind somethings and says: 
"This is a problem, we know the workarounds, but they don't work for us here and here, because this and this, we really like this to be improved."
Then the committee will know, they are fixing something, improving the language, not just adding a gimmick. 

This also goes for the proposals, they should really talk fixing problems and where workarounds fall short. 

Vicente J. Botet Escriba

unread,
Aug 18, 2018, 11:09:40 AM8/18/18
to std-pr...@isocpp.org, Thiago Macieira
Le 17/08/2018 à 17:05, Thiago Macieira a écrit :
> On Friday, 17 August 2018 07:46:42 PDT Vicente J. Botet Escriba wrote:
>> As this would request to compiler to store somewhere all the
>> declarations of a specific function, I believe that in order to don't
>> pay for what we don't use, we would need to opt in for the feature is
>> some specific way, that I don't worry about. E.g.
>>
>> void f(a:int, b:int);
>>
>> void f(x:int x, y:int);
>>
>> Only in this case the named parameters should be allowed.
> Agreed. To me, parameter names need to be opt-in. Anything else is simply not
> workable as the names have not and will continue not to be stable, so they
> aren't usable by users in the first place.
>
> But my question to you is: are those two function declarations the same
> function? If they are, how do you propose we declare them so that they are
> *not* the same function?
Yes, they declare the same function.

If we wan to have two different overload, we would need something else,
that don't need to be a named parameter. We could use now just tag types

struct tag1 {}

struct tag2 {}


void f(tag1, a:int, b:int);

void f(tag2, x:int, y:int);

Now we can call f using the tag objects

f(tag1{}, a:=1, b:=2);
f(tag2{}, x:=1, y:=2);


and we could add any syntactic sugar that make it easier to define those tags, and make them even local to the function

void f(@tag1, a:int, b:int);

void f(@tag2, x:int, y:int);


Now we can call f using the local tags

f(@tag1, a:=1, b:=2);
f(@tag2, x:=1, y:=2);

Note that tagx is not associated to any named parameter.


Vicente


Bo Persson

unread,
Aug 19, 2018, 4:27:33 AM8/19/18
to std-pr...@isocpp.org
We can of course already do this as f_tag1(1,2) and f_tag2(1,2), without
changing the language.

What is the advantage of f(@tag1 over f_tag1( ?


Bo Persson


David Brown

unread,
Aug 19, 2018, 7:23:06 AM8/19/18
to Matthew Woehlke, std-pr...@isocpp.org

On 17/08/18 21:11, Matthew Woehlke wrote:
> On 2018-08-17 14:42, David Brown wrote:
>> On 17/08/18 19:53, Matthew Woehlke wrote:
>>> Right now, changing the names of function parameters is SC+BC. IIUC, you
>>> are proposing to make it at least SIC, if not also BIC. I honestly can't
>>> see how you're going to convince the committee that that's okay.
>>
>> I'm sorry, I don't recognise the abbreviations SC, BC, SIC and BIC.
>
> S/B = source (API) / binary (ABI)
> C/IC = [in]compatible
>
> IOW, right now if I release a new version of my library that changes a
> parameter name, it has no effect. Under your proposal... does my
> previously compiled application (which uses said library) still run
> (BC)? Can I recompile it without needing to change its sources (SC)?
>

Let's skip the name parameter overloading bit for now. That is a new
feature, which would let you do something you could not do before, and
requires multiple changes (in compilers, and in source code that uses
it) to support. I have given a suggestion of how it could be
implemented, because it is a feature some people want - I am not
personally convinced that it is an important feature to have, and
certainly not convinced it is worth the price in therms of changes. So
here I am talking about the optional use of parameter names to improve
readability, help write correct code, help spot incorrect code, and -
optionally - to allow re-arranging of parameter order and skipping
defaults using names.

First, if you don't /use/ named parameters, then nothing (binary or
source) changes. That is important to my proposal.

A library generally consists of four parts - documentation, headers to
allow code to use the library, source of the implementation, and
binaries of the implementation. A release to others will have the first
two parts and one or both of the second two parts.

For code that uses the library, if they want to use named parameters
then the libraries headers need to be consistent about parameter names.
If the library documentation says that's okay, and considers the
parameter names to be part of the stable interface for the libraries,
then user could can use the names straight away - no library changes are
needed. (It is not uncommon for library documentation to be wholly or
partly generated automatically by tools like doxygen, which do view
parameter names as significant.) If the documentation does not say so,
then the user is taking a risk or tying their code to a specific library
release.

If you as the library implementer want to change a parameter name in the
headers, that's fair enough if you didn't say they would be consistent,
and a breaking change if you did - just like countless other kinds of
changes to a library API. If you want to change the parameter names
inside the implementation code, that's also fine. It would generally be
a bad idea to have them inconsistent with the headers, but it is
certainly allowable - the compiler would accept them, with optional
warnings.

I can't see how you would be any source or binary incompatibilities that
are not basically the same as you get with relying on any other kind of
implementation details that are not guaranteed to be fixed.



>>>    class foo
>>>    {
>>>      ...interface...
>>>    private:
>>>      FooPrivate* d;
>>>    };
>>>
>>> No so much "leaked" there...
>>
>> Well, no, not in a case like this.  But when the private section holds
>> members and possibly inline implementations, it is leaked.  This is
>> especially true of template libraries.
>
> True. The point is that "some libraries are designed poorly" is not an
> excuse to leak even *more*.
>

The use of PIMPLE is /not/ a black-or-white mark of good library design!

And the use of named parameters from the public header information does
not involve /any/ leak that was not already leaked from before.


>>> On 2018-08-17 13:12, David Brown wrote:
>>>> I don't think your argument against allowing people to use named
>>>> parameters for existing functions is at all convincing.
>>>
>>> Who's arguing that?
>>>
>>> I would make this perfectly legal:
>>>
>>>    void foo(int x); // note: not a named parameter
>>>    foo(.x=5); // okay, but maybe warn in pedantic mode
>>>    foo(.a=5); // okay, but probably warn
>>
>> Do you mean you would be happy to allow the parameter name in an
>> existing declaration to be used in function calls as a check?
>
> If I make a call with a named parameter that resolves to a function that
> does not have explicitly named parameters, then yes, I'm fine with the
> compiler *warning* me if the names don't match.
>

I agree.

>> But you would presumably not allow such names to be used for
>> overloads - that would require a specific extra syntax in the
>> declaration. Is that correct?
> Right.

Good.

>
> I guess in that sense what I'm proposing is opt-out weak naming (that
> only involves a possible compiler warning if you mismatch names) with
> opt-in strong naming, where the "fun stuff" (argument skipping, and per
> your question, name-based overloading) requires 'strong' naming.
>

Certainly I fully agree that name-based overloading requires "strong"
naming - where the declaration has a new syntax saying that names /must/
be used in some form, and where the parameter names become part of the
ABI for the function. (I hope that could be achieved with name
mangling, or with types somehow, in order to minimise the implementation
changes needed.)

I also think that common use, checked with optional compiler warnings
(which could of course be errors if you choose appropriate compiler
flags), can be done with "weak" naming - using parameter names declared
as they are today in existing code.

I'd perhaps prefer somewhat strong warnings or errors - using parameter
names in a call when the declaration had no names could be an optional
warning, but using parameter names incorrectly when the declaration has
names should likely be an error by default.

There are two other use-cases where there could be debate about the best
solution:

If the call has the right parameter names, but the wrong order, should
that be a warning, or error, or should the compiler "magically" correct
the order? If strong naming is used, the compiler should certainly be
able to re-order the parameters without giving any complaints. But with
weak naming, opinions are going to be more varied. I would like the
compiler to do the re-ordering, but I would be okay with an error
message - this would probably be the simplest and least risky solution.

The other case is for skipping arguments. Again, with strong naming
this should be supported. I believe it /could/ be supported fine with
weak naming, but would again be okay with the safer solution of not
allowing it without strong naming.



> The only way to opt out would be by not naming your parameters *at all*.
> However, I guess if you wrote:
>
> void foo(int x, int y);
> void foo(int a, int b);
>
> ...then *either* `foo(.a=1)` or `foo(.x=1)` should be accepted with no
> warning, and would call the same function. But this is all QoI stuff
> that doesn't need to be part of the standard.
>
>> I would be entirely happy with such a situation - my main objective is
>> to aid in writing correct and readable code, and my compiler (given
>> appropriate flags) would accept "foo(.x = 5)" without complaint while
>> warning about "foo(.a = 5)", I'd be very satisfied.
>>
>> I'd been even happier if it accepted this:
>>
>>     void foo(int x, int y);
>>     foo(.y = 1, .x = 2);    // Same as foo(2, 1)
>>
>> But if that's a problem, I'd be fine without it.
>
> Well, it would *accept* that... with a warning... but it would be the
> same as `foo(1, 2)` :-). If you wrote instead:
>
> void foo(int .x, int .y);
>
> ...then it would either do what you meant, or be a hard error. I'm on
> the fence whether reordering should be allowed. (And by "on the fence" I
> mean a) *I* don't feel strongly either way, and b) I think there are
> strong arguments for either case.)
>

That all seems fine to me. (I'm on the side that would /like/
reordering, but not far enough to feel it is a requirement. And I agree
about there being arguments both ways.)

Vicente J. Botet Escriba

unread,
Aug 19, 2018, 8:26:12 AM8/19/18
to std-pr...@isocpp.org, Bo Persson
Independently of the language feature, using a tag type allows to
transport the tag between different functions. In addition, you cannot
change the class constructor name, so you will need a tag to be able to
overload it. Take for example the in_place_t tag.

Vicente

mihailn...@gmail.com

unread,
Aug 19, 2018, 8:52:01 AM8/19/18
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
No, they can't:

What I have established, having had discussions about named arguments
on multiple occasions,
is that the following pain points are important:

1) it needs to be an opt-in for the author of a library, so no
automagic naming. Some library
authors do not want users relying on names that the library author
didn't expect them to rely
on, in order to avoid breakage that wasn't considered beforehand.

This was suggested back in 1991(!), and rejected!
We need to drop that idea for good and move on, make some progress!

And no, documenting which arguments are stable API is not good enough.
C++ is trying for 30 years to fix the need for documentation to use the code (concepts), no one needs more of it.

I am saying all this with best intentions - we need to stop repeating the same mistakes and really, step by step make some progress.
Progress in requirements (what we need), but progress in implementation (how can be done).

...

mihailn...@gmail.com

unread,
Aug 19, 2018, 9:04:50 AM8/19/18
to ISO C++ Standard - Future Proposals, b...@gmb.dk

Feel free to comment there, I am undecided on the topic. 

 

Vicente

Vicente J. Botet Escriba

unread,
Aug 19, 2018, 12:46:58 PM8/19/18
to std-pr...@isocpp.org, mihailn...@gmail.com, b...@gmb.dk
I'm not sure yet we need some syntactic sugar for this tag dispatching feature neither.
I'm just trying to separate the named parameter part from the overload part as orthogonal features.
You go too far for my taste in your proposal and you propose to have all at once. I'll be against such a feature.

Vicente

mihailn...@gmail.com

unread,
Aug 19, 2018, 1:22:52 PM8/19/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, b...@gmb.dk
Can you elaborate a bit more, what are your expectations and where should a line be drawn? As you can see in the comments to the post, there are similar concerns.
In any case it is not a proposal, but an investigation. It is clear tags have overlap with strong names, and if we can get an attractive proposition, based on tags, we can mark at least one part (the harder part?) of the named arguments equation "fixed". 


Vicente

Vicente J. Botet Escriba

unread,
Aug 19, 2018, 5:21:11 PM8/19/18
to std-pr...@isocpp.org, mihailn...@gmail.com, b...@gmb.dk
I would like to see the two features proposed independently.

I see more added value for weak named parameters that are optional and opt in on the author side (as the workarounds are not always friendly) than the strong named parameters that can be used to overload function or constructors and that are not optional (as tag types are an almost friendly library solution, as the standard library has showed already multiple times).

Vicente

Nicol Bolas

unread,
Aug 19, 2018, 5:53:57 PM8/19/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, b...@gmb.dk
The problem with having them be independent is that they're not independent. They're both playing around in the same space: invoking a parameter's name when you call the function.

The first question any named parameter proposal has to answer is whether to rely on the existing parameter name infrastructure or to use a special way to define them. There are arguments for both weak and strong parameters to want to have syntax to declare named parameters on a function. This is because, by having users rely on them to some degree (even if mis-matched naming is just a warning), you are making those parameter names part of the interface of the API. And that's different from how parameter names have been treated in the past.

If you try to develop weak and strong parameters completely independently, then what you can end up with are two proposals that provide completely different ways of declaring such parameters. And considering all of the stuff that we keep cramming into function declarations these days, giving parameters three names is utterly absurd. Even if you forbid weak parameter names on functions with strong names, that's still 3 separate syntaxes for declaring parameter names.

So I don't think it's reasonable to develop the two proposals completely independently from one another. They should at least be developed with the knowledge that the other is a possibility, and with syntax that can reasonably fit together.

Lastly, it should be noted that tagged dispatch doesn't help with what I will refer to as "mega-functions": functions that take stupidly large numbers of relatively independent parameters, but for some reason don't want to take them as a convenient aggregate. This is a use case that some people want to support, to avoid conceptual two-stage construction.

Jeremy Maitin-Shepard

unread,
Aug 19, 2018, 6:32:17 PM8/19/18
to ISO C++ Standard - Future Proposals
We already have a form of weak named parameters readily available in the form of /*parameter_name=*/value comments in combination with the clang-tidy bugprone-argument-comment check or similar functionality in other tools.

A mechanism that only warns and doesn't support forwarding/metaprogramming does not seem to me to provide much value over what we already have.

Nicol Bolas

unread,
Aug 19, 2018, 7:09:43 PM8/19/18
to ISO C++ Standard - Future Proposals


On Sunday, August 19, 2018 at 6:32:17 PM UTC-4, Jeremy Maitin-Shepard wrote:
We already have a form of weak named parameters readily available in the form of /*parameter_name=*/value comments in combination with the clang-tidy bugprone-argument-comment check or similar functionality in other tools.

A mechanism that only warns and doesn't support forwarding/metaprogramming does not seem to me to provide much value over what we already have.


And how would you propose to make "forwarding/metaprogramming" work with weak parameters? You can draw up a couple of simplistic cases that a compiler can peek through, but you can't come up with a coherent set of rules that work everywhere. And when I say "rules", I don't mean English text or "something like this"; I mean something approaching standards wording.

The fundamental issue with forwarding is about the transmission of information from source to destination. Strong parameter proposals transmit information in some explicit way: maybe it's a struct with known parameters or the names are themselves parameters or something else. Most strong parameter proposals handle parameter names in in ways C++ already understands.

With strong parameters, `emplace(val: 2)` compiles when `emplace(2)` does not because of normal C++ reasons. You're instantiating two different functions, with different sets of parameters, which therefore can have different behaviors.

Weak named parameters are intended to be purely notation information. Therefore, `emplace(val: 2)` must instantiate and execute the same template as `emplace(2)` does. Since the information is not passed in a channel that C++ currently understands, you now have to create an entirely new channel of information, which is somehow attached to a parameter yet otherwise is undetectable by anything that isn't the compiler. One which allows such intermediary functions to pass such named parameters to logging or other functions without causing a compile error.

Jeremy Maitin-Shepard

unread,
Aug 20, 2018, 1:11:20 AM8/20/18
to ISO C++ Standard - Future Proposals
Yes, it seems to me that weak named parameters are inherently incompatible with forwarding and metaprogramming, and for that reason I'm not really in favor of a weak named parameters mechanism being standardized.

Vicente J. Botet Escriba

unread,
Aug 20, 2018, 2:00:25 AM8/20/18
to std-pr...@isocpp.org, Jeremy Maitin-Shepard
Le 20/08/2018 à 00:32, Jeremy Maitin-Shepard a écrit :
We already have a form of weak named parameters readily available in the form of /*parameter_name=*/value comments in combination with the clang-tidy bugprone-argument-comment check or similar functionality in other tools.

A mechanism that only warns and doesn't support forwarding/metaprogramming does not seem to me to provide much value over what we already have.

If you find the clang tidy check is useful, the added value is that I wouldn't need clang-tidy to check for it and we would have the feature available with all the compliant compilers :)

I don't like these kind of comments. We could instead use attributes, but they are too heavyweight to my taste.


Vicente

Vicente J. Botet Escriba

unread,
Aug 20, 2018, 2:15:17 AM8/20/18
to std-pr...@isocpp.org, Nicol Bolas, mihailn...@gmail.com, b...@gmb.dk
Le 19/08/2018 à 23:53, Nicol Bolas a écrit :


On Sunday, August 19, 2018 at 5:21:11 PM UTC-4, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 19:22, mihailn...@gmail.com a écrit :


On Sunday, August 19, 2018 at 7:46:58 PM UTC+3, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 15:04, mihailn...@gmail.com a écrit :


On Sunday, August 19, 2018 at 3:26:12 PM UTC+3, Vicente J. Botet Escriba wrote:
Independently of the language feature, using a tag type allows to
transport the tag between different functions. In addition, you cannot
change the class constructor name, so you will need a tag to be able to
overload it. Take for example the in_place_t tag.


Feel free to comment there, I am undecided on the topic. 

 
I'm not sure yet we need some syntactic sugar for this tag dispatching feature neither.
I'm just trying to separate the named parameter part from the overload part as orthogonal features.
You go too far for my taste in your proposal and you propose to have all at once. I'll be against such a feature.

Can you elaborate a bit more, what are your expectations and where should a line be drawn? As you can see in the comments to the post, there are similar concerns.
In any case it is not a proposal, but an investigation. It is clear tags have overlap with strong names, and if we can get an attractive proposition, based on tags, we can mark at least one part (the harder part?) of the named arguments equation "fixed". 

I would like to see the two features proposed independently.

I see more added value for weak named parameters that are optional and opt in on the author side (as the workarounds are not always friendly) than the strong named parameters that can be used to overload function or constructors and that are not optional (as tag types are an almost friendly library solution, as the standard library has showed already multiple times).

The problem with having them be independent is that they're not independent. They're both playing around in the same space: invoking a parameter's name when you call the function.

The first question any named parameter proposal has to answer is whether to rely on the existing parameter name infrastructure or to use a special way to define them. There are arguments for both weak and strong parameters to want to have syntax to declare named parameters on a function. This is because, by having users rely on them to some degree (even if mis-matched naming is just a warning), you are making those parameter names part of the interface of the API. And that's different from how parameter names have been treated in the past.
I'm not for strong named parameters. For me, this is a disguised form of mixing weak named parameters and tag dispatching. This is why I believe the features are independent.
Now you can put them together and obtain named parameters and tag dispatch.


If you try to develop weak and strong parameters completely independently, then what you can end up with are two proposals that provide completely different ways of declaring such parameters. And considering all of the stuff that we keep cramming into function declarations these days, giving parameters three names is utterly absurd. Even if you forbid weak parameter names on functions with strong names, that's still 3 separate syntaxes for declaring parameter names.
If we want weak and strong named parameters and don't see another solution than having two syntaxes :(

I don't believe that we want non op in named parametrs, so each one of the weak/strong alternatives would need a specific syntax.



So I don't think it's reasonable to develop the two proposals completely independently from one another. They should at least be developed with the knowledge that the other is a possibility, and with syntax that can reasonably fit together.
Maybe you are right, but I would like that the proposal are clearly separated, so that we can be agains one from or the other.


Lastly, it should be noted that tagged dispatch doesn't help with what I will refer to as "mega-functions": functions that take stupidly large numbers of relatively independent parameters, but for some reason don't want to take them as a convenient aggregate. This is a use case that some people want to support, to avoid conceptual two-stage construction.
IMHO, this case is covered by weak named parameters already. 
If you accept that your signature changes and require that the argument must be named, strong named parameters could as well cover it.

If you don't accept that your signature changes and require that the argument must be named, weak named parameters + some static analysis tool (as clang-tidy) help could as well cover it (but not that the syantax will be already defined by the standard).

Maybe we need strong named parameters that don't change the signatures as well ;-)

Vicente

Vicente J. Botet Escriba

unread,
Aug 20, 2018, 2:18:56 AM8/20/18
to std-pr...@isocpp.org, Nicol Bolas, mihailn...@gmail.com, b...@gmb.dk
Le 19/08/2018 à 23:53, Nicol Bolas a écrit :


On Sunday, August 19, 2018 at 5:21:11 PM UTC-4, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 19:22, mihailn...@gmail.com a écrit :


On Sunday, August 19, 2018 at 7:46:58 PM UTC+3, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 15:04, mihailn...@gmail.com a écrit :


On Sunday, August 19, 2018 at 3:26:12 PM UTC+3, Vicente J. Botet Escriba wrote:
Independently of the language feature, using a tag type allows to
transport the tag between different functions. In addition, you cannot
change the class constructor name, so you will need a tag to be able to
overload it. Take for example the in_place_t tag.


Feel free to comment there, I am undecided on the topic. 

 
I'm not sure yet we need some syntactic sugar for this tag dispatching feature neither.
I'm just trying to separate the named parameter part from the overload part as orthogonal features.
You go too far for my taste in your proposal and you propose to have all at once. I'll be against such a feature.

Can you elaborate a bit more, what are your expectations and where should a line be drawn? As you can see in the comments to the post, there are similar concerns.
In any case it is not a proposal, but an investigation. It is clear tags have overlap with strong names, and if we can get an attractive proposition, based on tags, we can mark at least one part (the harder part?) of the named arguments equation "fixed". 

I would like to see the two features proposed independently.

I see more added value for weak named parameters that are optional and opt in on the author side (as the workarounds are not always friendly) than the strong named parameters that can be used to overload function or constructors and that are not optional (as tag types are an almost friendly library solution, as the standard library has showed already multiple times).

The problem with having them be independent is that they're not independent. They're both playing around in the same space: invoking a parameter's name when you call the function.

The first question any named parameter proposal has to answer is whether to rely on the existing parameter name infrastructure or to use a special way to define them. There are arguments for both weak and strong parameters to want to have syntax to declare named parameters on a function. This is because, by having users rely on them to some degree (even if mis-matched naming is just a warning), you are making those parameter names part of the interface of the API. And that's different from how parameter names have been treated in the past.
I'm not for strong named parameters. For me, this is a disguised form of mixing weak named parameters and tag dispatching. This is why I believe the features are independent.

Now you can put them together and obtain named parameters and tag dispatch.
If you try to develop weak and strong parameters completely independently, then what you can end up with are two proposals that provide completely different ways of declaring such parameters. And considering all of the stuff that we keep cramming into function declarations these days, giving parameters three names is utterly absurd. Even if you forbid weak parameter names on functions with strong names, that's still 3 separate syntaxes for declaring parameter names.
If we want weak and strong named parameters and don't see another solution than having two syntax :(

I don't believe that we want non op in named parameters, so each one of the weak/strong alternatives would need a specific syntax.



So I don't think it's reasonable to develop the two proposals completely independently from one another. They should at least be developed with the knowledge that the other is a possibility, and with syntax that can reasonably fit together.
Maybe you are right, but I would like that the proposal are clearly separated, so that we can be against one from or the other.


Lastly, it should be noted that tagged dispatch doesn't help with what I will refer to as "mega-functions": functions that take stupidly large numbers of relatively independent parameters, but for some reason don't want to take them as a convenient aggregate. This is a use case that some people want to support, to avoid conceptual two-stage construction.

mihailn...@gmail.com

unread,
Aug 20, 2018, 3:25:07 AM8/20/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, b...@gmb.dk
This is also my PoV in general, especially with more friendly tags. 
However, many people hate the idea names will be for weak only and they are left to use workarounds for overload resolution - after all, they consider names the correct way of handling this.

The second issue is, neither tags nor named functions are perfect solutions. Tags are a technically a working solution, but they don't convey the same meaning as label would. 
Natural naming like for:, to:, with:, if:, relativeTo:, ignoring:, including:, are pretty much unattainable for tags, both for the words that can be used and the lack of semicolon to give context when reading the code.

If we say "just use tags", people will still demand strong names, this is what I am trying to say. 

Sadly, strong names do not eliminate the need for tags! 
To still have tag as an idiom AND strong names, this is probably too much, but I might be wrong - 
after all if we eliminate static/free functions as constructors, it will be a massive win, even if there are some use for tags left.
This is what I mean by "where should we draw the line". 

 

Vicente

Nicol Bolas

unread,
Aug 20, 2018, 11:37:39 AM8/20/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
If that's the case, then we shouldn't have weak named parameters at all. Remember: weak named parameters are purely notational; they catch errors. That makes them a "nice to have". Strong named parameters provide genuine functionality, allowing you to express things that could not be expressed before.

If the design space is sufficiently constrained to only permit one or the other, then it should be the one that actually does something.

I don't believe that we want non op in named parametrs, so each one of the weak/strong alternatives would need a specific syntax.


So I don't think it's reasonable to develop the two proposals completely independently from one another. They should at least be developed with the knowledge that the other is a possibility, and with syntax that can reasonably fit together.
Maybe you are right, but I would like that the proposal are clearly separated, so that we can be agains one from or the other.

So, you can't be against one part of a proposal? The standards committee does it all the time.
Lastly, it should be noted that tagged dispatch doesn't help with what I will refer to as "mega-functions": functions that take stupidly large numbers of relatively independent parameters, but for some reason don't want to take them as a convenient aggregate. This is a use case that some people want to support, to avoid conceptual two-stage construction.
IMHO, this case is covered by weak named parameters already.

Then perhaps you're not understanding what a "mega-function" is. We're talking about functions with dozens of parameters, most of which are optional. Consider all of the various parameters you deal with when setting up a window. Window styles, size, position, hierarchy, font choice, and other things. Each of those are conceptually one or more parameters to the window creation function.

"Ideally", these would be literal parameters to a hypothetical Window class's constructor. But they're not; most such classes give relatively limited numbers of parameters to the Window's constructor. The rest are values set after the Window's construction, which means that defaults are used until those get set. Basically, it's two-stage construction.

The only way you can get the "ideal" case is if order for parameters is completely removed for such functions. You can specify the arguments in any order when you call them, and the caller can default any parameter with a default value simply by not specifying it.

Weak named parameters can't do that. Tag dispatch can't do that (not without extraordinary pain on the callee side). Even designated initializers have to be specified in the right order.

Nicol Bolas

unread,
Aug 20, 2018, 12:11:25 PM8/20/18
to ISO C++ Standard - Future Proposals, jer...@jeremyms.com
I think his point (and if it's not his, then it's mine) is that, if there's a way to just let implementations handle it outside of the language itself, then why not just do that?

Weak named parameters are pure notation. They exist to communicate information to the reader about what's going on. And that's all they do.

Weak named parameters do not communicate information to the implementation for the purpose of code generation. The only interaction the implementation has with them is to verify that the caller and callee used the same name. They merely ensure that the reader's information is accurate.

Weak named parameters do not change how you implement your functions, not on an API level. Since you can't overload based on weak parameters, you can't write `rectangle(top_left: ...)` as opposed to `rectangle(bottom_right: ...)`. You can express which convention the function uses, but you cannot allow the caller to select a convention.

Compare this to concepts and contracts. Concepts change what the language is doing. When you constrain a template, that does something to the template. It changes how code is generated (or more specifically, when it is not generated).

Contracts seem like notation, but they're not. Why? Pre-contracts, exceptions were sometimes used for logic errors; we even have `std::logic_error` as an exception base class. Once we have contracts in place, we can take stuff like that out, turning contract violations into actual contract violations.

Contracts change how you write code (or rather, make it easier for you to write code the way you should have been writing it). They allow you to specify a different kind of meaning in the language, compared to what you could have before.

Weak parameters change neither of these. There are very few C++ language features (outside of certain attributes) like this.

Given this fact, why should the committee spend time on something that is purely rotational? Don't they have more useful things to be getting on with?

mihailn...@gmail.com

unread,
Aug 20, 2018, 12:20:20 PM8/20/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
Are sure about that? Argument rearrangement is doable without changing the signature (a.k.a. strong) 

Where do you think it will fail? 

Nicol Bolas

unread,
Aug 20, 2018, 2:45:02 PM8/20/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
Maybe we have very different ideas about what "strong named parameters" means. The question of "changing the signature" is not the distinction between "strong" and "weak". At least, it's not the distinction that I'm talking about it.

"Weak named parameters" to me refers to named parameters which are all of the following:

1. Optional. You can call a function that has named parameters without naming the parameter(s) at the call site.
2. Ordered. The order of the arguments must match the parameter order of the function, regardless of the names you specify at the call site.
3. Overload-neutral. The presence or absence of names at the call site has no effect on function overloading.

Essentially, if your rules require that all named parameter function calls will invoke the same function with the same behavior if you stripped out all of the names, then the named parameter proposal is "weak".

Any named parameter feature which imposes rules antithetical to at least one of the above is "strong". With the strongest being the antithesis to all of them.

Hyman Rosen

unread,
Aug 20, 2018, 3:29:27 PM8/20/18
to std-pr...@isocpp.org
On Mon, Aug 20, 2018 at 2:45 PM Nicol Bolas <jmck...@gmail.com> wrote:
"Weak named parameters" to me refers to named parameters which are all of the following:

1. Optional. You can call a function that has named parameters without naming the parameter(s) at the call site.
2. Ordered. The order of the arguments must match the parameter order of the function, regardless of the names you specify at the call site.
3. Overload-neutral. The presence or absence of names at the call site has no effect on function overloading.

Essentially, if your rules require that all named parameter function calls will invoke the same function with the same behavior if you stripped out all of the names, then the named parameter proposal is "weak".

Any named parameter feature which imposes rules antithetical to at least one of the above is "strong". With the strongest being the antithesis to all of them.

Just for reference, Ada has
1.  Optional/Mixed: You may call a function with a number of leading positional arguments followed by named ones:
        my_proc(1, 2, 3, foo => 7, bar => 9);
2.  Not Ordered: named arguments can be specified in any order, after any ordered positional ones
3.  Affects Overloading:  See <https://www2.adacore.com/gap-static/GNAT_Book/html/node10.htm>

Ada has the caveat that "... operations that only differ in the names of the formal parameters,
but not their types, cannot be visible at the same point: either one hides the other through
scope and visibility rules, or else the declarations are illegal."  However, you can still have

    function F(A: Integer, B: Integer := 0) return Integer;
    function F(C: Integer                 ) return Integer;

and the call
f(5) will be ambiguous, while the call f(C => 5) will call the second function.

mihailn...@gmail.com

unread,
Aug 20, 2018, 4:18:34 PM8/20/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk


On Monday, August 20, 2018 at 9:45:02 PM UTC+3, Nicol Bolas wrote:


On Monday, August 20, 2018 at 12:20:20 PM UTC-4, mihailn...@gmail.com wrote:


On Monday, August 20, 2018 at 6:37:39 PM UTC+3, Nicol Bolas wrote:


On Monday, August 20, 2018 at 2:15:17 AM UTC-4, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 23:53, Nicol Bolas a écrit :


On Sunday, August 19, 2018 at 5:21:11 PM UTC-4, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 19:22, mihailn...@gmail.com a écrit :


On Sunday, August 19, 2018 at 7:46:58 PM UTC+3, Vicente J. Botet Escriba wrote:
Le 19/08/2018 à 15:04, mihailn...@gmail.com a écrit :


On Sunday, August 19, 2018 at 3:26:12 PM UTC+3, Vicente J. Botet Escriba wrote:
Independently of the language feature, using a tag type allows to
transport the tag between different functions. In addition, you cannot
change the class constructor name, so you will need a tag to be able to
overload it. Take for example the in_place_t tag.


Feel free to comment there, I am undecided on the topic. 

 
I'm not sure yet we need some syntactic sugar for this tag dispatching feature neither.
I'm just trying to separate the named parameter part from the overload part as orthogonal features.
You go too far for my taste in your proposal and you propose to have all at once. I'll be against such a feature.
...
Lastly, it should be noted that tagged dispatch doesn't help with what I will refer to as "mega-functions": functions that take stupidly large numbers of relatively independent parameters, but for some reason don't want to take them as a convenient aggregate. This is a use case that some people want to support, to avoid conceptual two-stage construction.
IMHO, this case is covered by weak named parameters already.

 
Then perhaps you're not understanding what a "mega-function" is. We're talking about functions with dozens of parameters, most of which are optional. Consider all of the various parameters you deal with when setting up a window. Window styles, size, position, hierarchy, font choice, and other things. Each of those are conceptually one or more parameters to the window creation function.

"Ideally", these would be literal parameters to a hypothetical Window class's constructor. But they're not; most such classes give relatively limited numbers of parameters to the Window's constructor. The rest are values set after the Window's construction, which means that defaults are used until those get set. Basically, it's two-stage construction.

The only way you can get the "ideal" case is if order for parameters is completely removed for such functions. You can specify the arguments in any order when you call them, and the caller can default any parameter with a default value simply by not specifying it.

Weak named parameters can't do that. Tag dispatch can't do that (not without extraordinary pain on the callee side). Even designated initializers have to be specified in the right order.

 
Are sure about that? Argument rearrangement is doable without changing the signature (a.k.a. strong)

Maybe we have very different ideas about what "strong named parameters" means. The question of "changing the signature" is not the distinction between "strong" and "weak". At least, it's not the distinction that I'm talking about it.

"Weak named parameters" to me refers to named parameters which are all of the following:

1. Optional. You can call a function that has named parameters without naming the parameter(s) at the call site.
2. Ordered. The order of the arguments must match the parameter order of the function, regardless of the names you specify at the call site.
3. Overload-neutral. The presence or absence of names at the call site has no effect on function overloading.

Essentially, if your rules require that all named parameter function calls will invoke the same function with the same behavior if you stripped out all of the names, then the named parameter proposal is "weak".

Any named parameter feature which imposes rules antithetical to at least one of the above is "strong". With the strongest being the antithesis to all of them.

I see, for you weak refers to the notion the call is the same, but unchecked, without them.

For me strong is referring to "does it introduce a new signature", the same way strong typedef introduces a new type. Everything else is weak as it is about the compiler not the code.

Changing the signature is a clear boundary in both semantics and implementation.  

But in the realm of non-changing-the-signature names, a lot can be done, granted the calls will differ w/ and w/o names. 
Should it be done is a different matter, this is a debate as heated as the one about signature - some people consider names useless if they don't rearrange/skip defaults, some are happy with just checking alone.  

Vicente J. Botet Escriba

unread,
Aug 20, 2018, 6:21:51 PM8/20/18
to std-pr...@isocpp.org, Nicol Bolas, mihailn...@gmail.com, b...@gmb.dk
Well, I believe that if the design space is limited, I prefer to have named parameters that don't change the signature.

The other I can do them with tag types :)

You see, we disagree.


I don't believe that we want non op in named parametrs, so each one of the weak/strong alternatives would need a specific syntax.


So I don't think it's reasonable to develop the two proposals completely independently from one another. They should at least be developed with the knowledge that the other is a possibility, and with syntax that can reasonably fit together.
Maybe you are right, but I would like that the proposal are clearly separated, so that we can be agains one from or the other.

So, you can't be against one part of a proposal? The standards committee does it all the time.
Better to split them. For me of course.

Lastly, it should be noted that tagged dispatch doesn't help with what I will refer to as "mega-functions": functions that take stupidly large numbers of relatively independent parameters, but for some reason don't want to take them as a convenient aggregate. This is a use case that some people want to support, to avoid conceptual two-stage construction.
IMHO, this case is covered by weak named parameters already.

Then perhaps you're not understanding what a "mega-function" is. We're talking about functions with dozens of parameters, most of which are optional. Consider all of the various parameters you deal with when setting up a window. Window styles, size, position, hierarchy, font choice, and other things. Each of those are conceptually one or more parameters to the window creation function.
Maybe you don't understand the same thing as me. week named parameters allow optionals for me :)


"Ideally", these would be literal parameters to a hypothetical Window class's constructor. But they're not; most such classes give relatively limited numbers of parameters to the Window's constructor. The rest are values set after the Window's construction, which means that defaults are used until those get set. Basically, it's two-stage construction.

The only way you can get the "ideal" case is if order for parameters is completely removed for such functions. You can specify the arguments in any order when you call them, and the caller can default any parameter with a default value simply by not specifying it.

Weak named parameters can't do that. Tag dispatch can't do that (not without extraordinary pain on the callee side). Even designated initializers have to be specified in the right order.
Why are you so sure my weak named parameters can't do that?

Vicente

Vicente J. Botet Escriba

unread,
Aug 20, 2018, 6:27:04 PM8/20/18
to std-pr...@isocpp.org, Nicol Bolas, mihailn...@gmail.com, b...@gmb.dk
Maybe it is not for you. It is for me and others. Another distinction that we have not discused so much, is if the names are needed for the arguments or are optional.

"Weak named parameters" to me refers to named parameters which are all of the following:

1. Optional. You can call a function that has named parameters without naming the parameter(s) at the call site.
2. Ordered. The order of the arguments must match the parameter order of the function, regardless of the names you specify at the call site.
Why? This is a simplification, that could be wished or not.
3. Overload-neutral. The presence or absence of names at the call site has no effect on function overloading.
So it doesn't change the function signature.

4. You are missing that we can skip some of them.


Essentially, if your rules require that all named parameter function calls will invoke the same function with the same behavior if you stripped out all of the names, then the named parameter proposal is "weak".

Any named parameter feature which imposes rules antithetical to at least one of the above is "strong". With the strongest being the antithesis to all of them.
You should write a proposal so that we know what you are talking of :)


Vicente

Nicol Bolas

unread,
Aug 20, 2018, 9:52:56 PM8/20/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
That's not what you said earlier:

Also, this obviously does not address the named arguments, that are optional, the "weak" ones, so this solution will not make everyone happy.

Emphasis added. And there's this post:

//weak argument names, not mandatory
...
//strong argument name, adds an overload, not ignorable

Yes, that post is talking about how you would declare weak names differently from strong names, but it's also talking about how they get used at the call site: "not mandatory" vs. "not ignorable".

Changing the signature is a clear boundary in both semantics and implementation.
But in the realm of non-changing-the-signature names, a lot can be done, granted the calls will differ w/ and w/o names.

I think you're too focused on signature as the notion of a function's identity. There are aspects of functions which are not their signature yet directly relate to its identity.

Return values. If you declare two functions with the same name and signatures with different return values, then you get a compile error. `noexcept` works that way too; you can't be inconsistent about that. Both of these are part of the function's type, but not its signature (though `noexcept` can be cast away).

Contracts affect neither type nor signature, but even that requires some basic consistency. You can declare contracts on a function or not. But all contract declarations for the same function must be consistent.

If a parameter name can be used as a syntactic modifier when calling the function (for the purposes of this discussion, anything which affects actual code generation. This includes reordering of arguments), then that name ought to be, if not part of the function's type, then at least have as much consistency as its contracts. That is, if you want to be able to reorder parameters, then the function should consistently assign each parameter a single name or a single "unnamed".

If names can be inconsistent and still affect syntax, bad things result.

mihailn...@gmail.com

unread,
Aug 21, 2018, 4:04:26 AM8/21/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk


On Tuesday, August 21, 2018 at 4:52:56 AM UTC+3, Nicol Bolas wrote:
On Monday, August 20, 2018 at 4:18:34 PM UTC-4, mihailn...@gmail.com wrote:
On Monday, August 20, 2018 at 9:45:02 PM UTC+3, Nicol Bolas wrote:
On Monday, August 20, 2018 at 12:20:20 PM UTC-4, mihailn...@gmail.com wrote:
Are sure about that? Argument rearrangement is doable without changing the signature (a.k.a. strong)

Maybe we have very different ideas about what "strong named parameters" means. The question of "changing the signature" is not the distinction between "strong" and "weak". At least, it's not the distinction that I'm talking about it.

"Weak named parameters" to me refers to named parameters which are all of the following:

1. Optional. You can call a function that has named parameters without naming the parameter(s) at the call site.
2. Ordered. The order of the arguments must match the parameter order of the function, regardless of the names you specify at the call site.
3. Overload-neutral. The presence or absence of names at the call site has no effect on function overloading.

Essentially, if your rules require that all named parameter function calls will invoke the same function with the same behavior if you stripped out all of the names, then the named parameter proposal is "weak".

Any named parameter feature which imposes rules antithetical to at least one of the above is "strong". With the strongest being the antithesis to all of them.

I see, for you weak refers to the notion the call is the same, but unchecked, without them.

For me strong is referring to "does it introduce a new signature", the same way strong typedef introduces a new type. Everything else is weak as it is about the compiler not the code.

That's not what you said earlier:

Also, this obviously does not address the named arguments, that are optional, the "weak" ones, so this solution will not make everyone happy.

Emphasis added. And there's this post:

//weak argument names, not mandatory
...
//strong argument name, adds an overload, not ignorable

Yes, that post is talking about how you would declare weak names differently from strong names, but it's also talking about how they get used at the call site: "not mandatory" vs. "not ignorable".

There is no contradiction, they are optional, and ignorable by the user (he might not use them). 
Now, it depends on how much features they have, they might also be as weak as you assume they are, so they are (can be) ignored by the compiler as well and the call is the same.

Most of the time here we talked about these weakest ones, and considered "extra features" as "bonuses".

For me personally strong were always the one changing the signature, that's why the first post about tags-as-labels was called "Strong named arguments as tag overloads"

And weak for me were not rearranging or skipping, but having defaulted-by-naming f(a:, b:, 10) 
 
Of course even these are not ignorable as the call will fail without them. Probably using the word ignorable was not correct. 

In any case weak names are optional. 
This is not to say, they do not affect overloading, they do, but only on the call site.

void func(int a, int b);
void func(int a, int b: b=1, int c: c=2);

func(1, 3); //< overload 1
func(1, b: 3); //< overload 2
func(1, c: 3); //< overload 2


Changing the signature is a clear boundary in both semantics and implementation.
But in the realm of non-changing-the-signature names, a lot can be done, granted the calls will differ w/ and w/o names.

I think you're too focused on signature as the notion of a function's identity. There are aspects of functions which are not their signature yet directly relate to its identity.

Return values. If you declare two functions with the same name and signatures with different return values, then you get a compile error. `noexcept` works that way too; you can't be inconsistent about that. Both of these are part of the function's type, but not its signature (though `noexcept` can be cast away).

Contracts affect neither type nor signature, but even that requires some basic consistency. You can declare contracts on a function or not. But all contract declarations for the same function must be consistent.

If a parameter name can be used as a syntactic modifier when calling the function (for the purposes of this discussion, anything which affects actual code generation. This includes reordering of arguments), then that name ought to be, if not part of the function's type, then at least have as much consistency as its contracts. That is, if you want to be able to reorder parameters, then the function should consistently assign each parameter a single name or a single "unnamed".


In the original post about weak arguments, I suggested they obey the rules of default arguments - you name your arguments only in the first declaration. No names afterwards, not even the same. 

auto child(name mother: mom, name dad); 

// auto child(name mother: mom, name dad); //< error
// auto child(name mom, name father: dad); //< error
auto child(name mutter, vater); //< OK
auto child(name mother, name father) //< OK
  // implementation
}
 
This seems the be the most consistent approach as (weak) named arguments ultimately inhabit exactly the same design space as default arguments - a compiler helper.

With default arguments the compiler is only obligated to copy-paste the default expression if we don't pass a value.

With names it is a matter of what features we want to have:
  • to only name-check
  • to paste in place of name-only
  • to paste on omission
  • to rearrange

Nicol Bolas

unread,
Aug 22, 2018, 4:47:29 PM8/22/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
If names are important enough to affect what overload gets called, then they ought to be part of a function's type. If they are truly "optional", then they should not be able to affect overloading.

To me, "optional" means exactly that: optional. The choice of whether to use names or not is entirely the prerogative of the caller. A caller should be able to call the same function, providing the same parameters, without using parameter names if they so desire.

You call the second overload, passing it the first two parameters. That is not possible without using parameter names. You call the third overload, passing only the first and third parameters. That too is not possible without using parameter names.

For your proposal, parameter names are not optional. That's different from saying that they are mandatory. There are cases in your design where you can call a named parameter function overload without providing names for those parameters.

The key here is that in your proposal, parameter names are part of the overload set's interface. Just like the various conversion operators and constructors on the types used as parameters.

That's what makes them different from default parameters: the fact that `func(1, 3)` is not ambiguous. With default parameters, users who don't specify all of the parameters can create ambiguous call relative to other overloads in the set. With your design, that doesn't happen. This is what makes the parameter name more than just a default argument; it makes it part of the overload set's interface.

Changing the signature is a clear boundary in both semantics and implementation.
But in the realm of non-changing-the-signature names, a lot can be done, granted the calls will differ w/ and w/o names.

I think you're too focused on signature as the notion of a function's identity. There are aspects of functions which are not their signature yet directly relate to its identity.

Return values. If you declare two functions with the same name and signatures with different return values, then you get a compile error. `noexcept` works that way too; you can't be inconsistent about that. Both of these are part of the function's type, but not its signature (though `noexcept` can be cast away).

Contracts affect neither type nor signature, but even that requires some basic consistency. You can declare contracts on a function or not. But all contract declarations for the same function must be consistent.

If a parameter name can be used as a syntactic modifier when calling the function (for the purposes of this discussion, anything which affects actual code generation. This includes reordering of arguments), then that name ought to be, if not part of the function's type, then at least have as much consistency as its contracts. That is, if you want to be able to reorder parameters, then the function should consistently assign each parameter a single name or a single "unnamed".


In the original post about weak arguments, I suggested they obey the rules of default arguments - you name your arguments only in the first declaration. No names afterwards, not even the same. 

auto child(name mother: mom, name dad); 

// auto child(name mother: mom, name dad); //< error
// auto child(name mom, name father: dad); //< error
auto child(name mutter, vater); //< OK
auto child(name mother, name father) //< OK
  // implementation
}
 
This seems the be the most consistent approach as (weak) named arguments ultimately inhabit exactly the same design space as default arguments - a compiler helper.

Default arguments generate code (conditionally or not). Weak named arguments do not. I have some difficulty considering those to be "exactly the same design space". Or even similar design spaces.

Nicol Bolas

unread,
Aug 22, 2018, 4:57:05 PM8/22/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
On Wednesday, August 22, 2018 at 4:47:29 PM UTC-4, Nicol Bolas wrote:
Default arguments generate code (conditionally or not). Weak named arguments do not. I have some difficulty considering those to be "exactly the same design space". Or even similar design spaces.

Disregard this paragraph; I meant to delete that before pressing "send".
Message has been deleted

mihailn...@gmail.com

unread,
Aug 23, 2018, 4:37:20 AM8/23/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, mihailn...@gmail.com, b...@gmb.dk
 
Boy, I made a mistake, func(1, 3) is of course ambiguous! 

Bad example, here is the correct one:

void func(int a, string b);
void func(int a, int b: b=1, string c: c="hi");

void func(int a, int b: b=1, int c: c=2);

func(1, "bye"); //< overload 1
func(1, c: "bye"); //< overload 2
func(1, c: 3); //< overload 3

The point was, on the call site (optional) names can affect overloading. 

The more general argument was, optional names can service mega-functions, as long as they allow skipping defaulted arguments.
But even mega-functions aside, none of the current alternatives to the above code are better, even if possible - that's is way people want that feature!

Matthew Woehlke

unread,
Aug 23, 2018, 12:44:42 PM8/23/18
to std-pr...@isocpp.org, David Brown
On 2018-08-19 07:23, David Brown wrote:
> here I am talking about the optional use of parameter names to improve
> readability, help write correct code, help spot incorrect code, and -
> optionally - to allow re-arranging of parameter order and skipping
> defaults using names.

If we *don't* get the optional parts (skipping, reordering)... then
what's the point?

If we don't get the optional parts, you might as well modify your
compiler to warn you about:

void foo(int x);
foo(/*y=*/5); // warning: 'y' != 'x'

...and no language change is required.

> There are two other use-cases where there could be debate about the best
> solution:
>
> If the call has the right parameter names, but the wrong order, should
> that be a warning, or error, or should the compiler "magically" correct
> the order?  If strong naming is used, the compiler should certainly be
> able to re-order the parameters without giving any complaints.

Honestly, this is a good question. There are arguments on both sides
whether or not to allow reordering, and I am somewhat avoiding taking a
stance.

That said, FWIW I think I'm less okay with reordering weak names than
strong names.

> The other case is for skipping arguments.  Again, with strong naming
> this should be supported.  I believe it /could/ be supported fine with
> weak naming, but would again be okay with the safer solution of not
> allowing it without strong naming.

Agreed. (Skipping is sort of a "weak" form of reordering.)

--
Matthew

David Brown

unread,
Aug 23, 2018, 1:17:04 PM8/23/18
to Matthew Woehlke, std-pr...@isocpp.org


On 23/08/18 18:44, Matthew Woehlke wrote:
> On 2018-08-19 07:23, David Brown wrote:
>> here I am talking about the optional use of parameter names to improve
>> readability, help write correct code, help spot incorrect code, and -
>> optionally - to allow re-arranging of parameter order and skipping
>> defaults using names.
>
> If we *don't* get the optional parts (skipping, reordering)... then
> what's the point?
>
> If we don't get the optional parts, you might as well modify your
> compiler to warn you about:
>
> void foo(int x);
> foo(/*y=*/5); // warning: 'y' != 'x'
>
> ...and no language change is required.

I see two disadvantages of that. One is that it is seriously ugly
having comments in the middle of a function call - "foo(.y = 5)" is very
much nicer and neater syntax. The other is that because there is no
standard, and neither limits nor requirements in what you might have in
these comments (maybe someone wants to write "foo( /* was 4 in last
version */ 5);", it would be hard to get it in common use.

But as far as I see it, it is this checking that is the most important
feature. It covers the two most vital points - it aids readability and
clarity of the code, and it means mistakes are more likely to be caught
at compile-time. These are both /good/ things - and IMHO they are worth
allowing the syntax "foo(.y = 5);" even if you don't get anything else.

(I'd like to see that same syntax in C as well, including calling C
functions from C++ - and I think re-ordering and overloading would not
be a consideration for C.)

>
>> There are two other use-cases where there could be debate about the best
>> solution:
>>
>> If the call has the right parameter names, but the wrong order, should
>> that be a warning, or error, or should the compiler "magically" correct
>> the order?  If strong naming is used, the compiler should certainly be
>> able to re-order the parameters without giving any complaints.
>
> Honestly, this is a good question. There are arguments on both sides
> whether or not to allow reordering, and I am somewhat avoiding taking a
> stance.
>

I have some rough preferences here - but I freely admit that I don't
have the experience at guessing what the complications might be, and I
leave it to the experts to see if the stance is forced by issues that I
have not thought about.

Matthew Woehlke

unread,
Aug 23, 2018, 1:18:57 PM8/23/18
to std-pr...@isocpp.org, Nicol Bolas, mihailn...@gmail.com, b...@gmb.dk
On 2018-08-19 17:53, Nicol Bolas wrote:
> Lastly, it should be noted that tagged dispatch doesn't help with what I
> will refer to as "mega-functions": functions that take stupidly large
> numbers of relatively independent parameters, but for some reason don't
> want to take them as a convenient aggregate. This is a use case that some
> people want to support, to avoid conceptual two-stage construction.

A good exercise in "why do we want named parameters" (at least for this
use case) would be to try to implement the API of Python's argparse in
C++17.

--
Matthew

Matthew Woehlke

unread,
Aug 23, 2018, 1:31:51 PM8/23/18
to std-pr...@isocpp.org, Nicol Bolas
On 2018-08-19 19:09, Nicol Bolas wrote:
> On Sunday, August 19, 2018 at 6:32:17 PM UTC-4, Jeremy Maitin-Shepard wrote:
>> We already have a form of weak named parameters readily available in the
>> form of /*parameter_name=*/value comments in combination with the
>> clang-tidy bugprone-argument-comment check or similar functionality in
>> other tools.
>>
>> A mechanism that only warns and doesn't support forwarding/metaprogramming
>> does not seem to me to provide much value over what we already have.
>
> And how would you propose to make "forwarding/metaprogramming" work with
> weak parameters? You can draw up a couple of simplistic cases that a
> compiler can peek through, but you can't come up with a coherent set of
> rules that work everywhere. And when I say "rules", I don't mean English
> text or "something like this"; I mean something approaching standards
> wording.

I don't know about "something approaching standards wording", but where
I was headed, this:

void foo(int y);
std::invoke(foo, .x=5);

...would turn into something like:

invoke(Func *foo, arg<int, "x"> a1 = 5)
{
(*foo)(a1);
}

...which could warn about decomposing the named argument "x" into a call
that names its argument "y".

So, basically, I implement "weak" names by allowing the caller to pass a
strongly named argument to a function that takes only weak names.
Delaying the decay from strongly-named to weakly-named allows most of
the forwarding cases to work.

> The fundamental issue with forwarding is about the transmission of
> information from source to destination. Strong parameter proposals transmit
> information in some explicit way: maybe it's a struct with known parameters
> or the names are themselves parameters or something else. Most strong
> parameter proposals handle parameter names in in ways C++ already
> understands.

Right. In the example above, the *caller* passes a strongly-named
argument, which is forwarded as a strongly named argument. It only
"loses" the naming information when it hits a function taking a
non-template parameter that is not strongly named. At that point, the
compiler has the opportunity to issue a warning.

> With strong parameters, `emplace(val: 2)` compiles when `emplace(2)` does
> not because of normal C++ reasons. You're instantiating two different
> functions, with different sets of parameters, which therefore can have
> different behaviors.

...except that in my thinking, `emplace(2)` will auto-promote to a
strongly named argument, if that's what `emplace` wants. You only get an
error if the caller provides a strongly-named argument that the callee
does not accept.

void foo(int .x);
foo(5); // okay
foo(.x=5); // okay
foo(.y=5); // error

> Weak named parameters are intended to be purely notation information.
> Therefore, `emplace(val: 2)` must instantiate and execute the same template
> as `emplace(2)` does. Since the information is not passed in a channel that
> C++ currently understands, you now have to create an entirely new channel
> of information, which is somehow attached to a parameter yet otherwise is
> undetectable by anything that isn't the compiler. One which allows such
> intermediary functions to pass such named parameters to logging or other
> functions without causing a compile error.

It's not "undetectable". OTOH, if you have:

void foo(int x);
foo(5); // okay
foo(.x=5); // okay

...by the time you're in `foo`, you've lost the name information.
Forwarding works if you accept a template type. If you accept a concrete
type, the name is lost.

--
Matthew

Matthew Woehlke

unread,
Aug 23, 2018, 1:37:10 PM8/23/18
to std-pr...@isocpp.org, David Brown
On 2018-08-23 13:17, David Brown wrote:
> On 23/08/18 18:44, Matthew Woehlke wrote:
>> If we don't get the optional parts, you might as well modify your
>> compiler to warn you about:
>>
>>    void foo(int x);
>>    foo(/*y=*/5); // warning: 'y' != 'x'
>>
>> ...and no language change is required.
>
> I see two disadvantages of that.  One is that it is seriously ugly
> having comments in the middle of a function call - "foo(.y = 5)" is very
> much nicer and neater syntax.  The other is that because there is no
> standard, and neither limits nor requirements in what you might have in
> these comments (maybe someone wants to write "foo( /* was 4 in last
> version */ 5);", it would be hard to get it in common use.
>
> But as far as I see it, it is this checking that is the most important
> feature.  It covers the two most vital points - it aids readability and
> clarity of the code, and it means mistakes are more likely to be caught
> at compile-time.  These are both /good/ things - and IMHO they are worth
> allowing the syntax "foo(.y = 5);" even if you don't get anything else.

I understand what you're saying. I just happen to strongly disagree :-).
(That is, I would agree with the above if you replace "most" with
"least"...)

That said, my current vision would give you what you're asking for...
but also overloading and skipping.

Also, as has been elsewhere discussed, I'm not sure how forwarding would
work without at least some degree of "strong" naming, and I don't see
any proposal getting much traction if it doesn't "solve" forwarding.

--
Matthew

Matthew Woehlke

unread,
Aug 23, 2018, 3:00:22 PM8/23/18
to std-pr...@isocpp.org, Nicol Bolas
On 2018-08-20 11:37, Nicol Bolas wrote:
> The only way you can get the "ideal" case is if order for parameters is *completely
> removed* for such functions. You can specify the arguments in any order
> when you call them, and the caller can default any parameter with a default
> value simply by not specifying it.

Um... why? Why is it a problem to enforce that, if you specify a font,
that has to be specified before a palette?

You need to be able to *skip* parameters. I don't see why you need to be
able to *reorder* parameters...

--
Matthew

Matthew Woehlke

unread,
Aug 23, 2018, 4:27:01 PM8/23/18
to std-pr...@isocpp.org, Justin Bassett
(Sorry, I'm only just now reading the original post...)

On 2018-08-16 01:10, Justin Bassett wrote:
> However, I believe that the name should be part of the function type and
> should be mangled into the name for the function. It should not be allowed
> for the following two declarations to coexist:
>
> int foo(int .x, int .y);
> int foo(int .a, int .b);
>
> If the name of the parameters are not part of the function, then if those
> declarations are in separate translation units, there's little way to
> enforce that this should fail to compile / link.

If the name is part of the ABI, *why* should the above be forbidden? For
that matter, *how* would you forbid it? They have different mangled names...

If the two exist only in separate TU's and are never seen in the same
TU, you would be requiring the *linker*, not the compiler, to reject this.

> This does bring in the issue that it becomes harder to change existing APIs
> to accept named parameters in an ABI compatible manner.

FWIW, my current thought is to solve this by means of an explicitly
inline trampoline function. (Actually, I would ideally make it an
*alias*, not a "real" trampoline at all.)

For example:

void foo(int);
using foo(int .x) = foo(int);

Basically, it is a mechanism to add a function prototype to the overload
set, such that the added overload actually resolves to a different ABI
function. Thus, you can write code *as if* `foo(int .x)` existed, but
when the compiler resolves a call to the named argument version, it will
*actually* call `foo(int)`. This allows the ABI to be unchanged.

> Now for templates. In order to have higher-order functions which work on
> named parameters, I believe it is necessary to have a new kind of template
> parameter, something like this:
>
> // I separated the name completely from the type.
> // This would severely complicate generic code, however, so maybe there's
> // a way to keep both together
> template <typename F, .typename Name, typename Arg>
> void call(F fn, Arg&& .Name arg) {
> // Some syntax to expand the name is needed. I'm not sure what
> // a reasonable choice is. There should also probably be a way
> // to generate a name from a compile time string.
> fn(.(Name) = std::forward<Arg>(arg));
> }

...which suggests that e.g. std::invoke can't use named parameters. I
don't think that will work.

> Reflection could possibly allow us to inspect the actual name supplied
> (useful for libraries such as fmt: fmt::format("({x}, {y})", .x = 10, .y =
> 20); ), and it would probably be useful to be able to generate a name from
> a compile-time string.

I've been thinking along the lines of named parameters being syntax
sugar for something like `std::arg<T, name>`, where `name` is a string
literal type. So reflection would just see the std::arg, and we don't
have to think about this too hard (or about ABI).

(Not *just* syntax sugar; they affect overload resolution also. But for
e.g. reflection, they'd just be sugar.)

I *think* this makes your above example possible, though it may require
something like building a dictionary by converting the compile-time
literals into (possibly) run-time keys.

> Further ideas: I don't want to propose this yet, but I would also like to
> see named parameters in template parameters. For example, it would be nice
> to be easily able to specify the allocator of an unordered_map:
> std::unordered_map<Key,
> Value, .allocator = MyAllocator> . I believe this would break ABI
> compatibility, though.

I think this is an orthogonal space, much closer actually to designated
initializers. (Yes, it *looks* very much the same to users, but
implementation-wise I don't think it has anything like the same sorts of
problems as genuine named *function arguments*.)

This would be a good space for an independent proposal to explore.

--
Matthew

David Brown

unread,
Aug 24, 2018, 2:31:51 AM8/24/18
to Matthew Woehlke, std-pr...@isocpp.org


On 23/08/2018 19:37, Matthew Woehlke wrote:
> On 2018-08-23 13:17, David Brown wrote:
>> On 23/08/18 18:44, Matthew Woehlke wrote:
>>> If we don't get the optional parts, you might as well modify your
>>> compiler to warn you about:
>>>
>>>    void foo(int x);
>>>    foo(/*y=*/5); // warning: 'y' != 'x'
>>>
>>> ...and no language change is required.
>>
>> I see two disadvantages of that.  One is that it is seriously ugly
>> having comments in the middle of a function call - "foo(.y = 5)" is very
>> much nicer and neater syntax.  The other is that because there is no
>> standard, and neither limits nor requirements in what you might have in
>> these comments (maybe someone wants to write "foo( /* was 4 in last
>> version */ 5);", it would be hard to get it in common use.
>>
>> But as far as I see it, it is this checking that is the most important
>> feature.  It covers the two most vital points - it aids readability and
>> clarity of the code, and it means mistakes are more likely to be caught
>> at compile-time.  These are both /good/ things - and IMHO they are worth
>> allowing the syntax "foo(.y = 5);" even if you don't get anything else.
>
> I understand what you're saying. I just happen to strongly disagree :-).
> (That is, I would agree with the above if you replace "most" with
> "least"...)

I think we just have a little difference in perspective. You want
powerful new features, and increased checking and readability is a free
bonus from that. I want increased checking and readability, and new
features might be a bonus from the way they are implemented.

>
> That said, my current vision would give you what you're asking for...
> but also overloading and skipping.
>
> Also, as has been elsewhere discussed, I'm not sure how forwarding would
> work without at least some degree of "strong" naming, and I don't see
> any proposal getting much traction if it doesn't "solve" forwarding.
>

We could get /my/ part done without worrying about forwarding...

But I agree it is unlikely that the standards would support a named
parameter system that doesn't cover a wider range of new functions.
It is loading more messages.
0 new messages