semicolon instead of <> to delimit template args. std::get(1;tup) instead of std::get<1>(tup)

402 views
Skip to first unread message

Aaron McDaid

unread,
Oct 13, 2017, 5:13:56 PM10/13/17
to ISO C++ Standard - Future Proposals
Instead of having to type

    std::get<1>(tup);


I would like to be able to type this instead:


std::get(1; tup);

The initial motivation is simply to make some metaprogramming look more "function-like":

is_same<T, U>{}

could become

is_same(T, U;)

which is more readable as T and U really are intended as the arguments to that (meta-)function

It will save a few characters, and could cut down on some ambiguities around '<'/'>' for 'less than'/'greater than'.

I've put a short little document on github, I'll try to incorporate feedback into this:

https://github.com/aaronmcdaid/semicolons.and.template.args/blob/master/README.md

Also, I like this

auto v = vector<int>(5); // current form auto v = vector(int; 5); // new form

and

auto v = vector<int>{2,3,5,7}; // current form auto v = vector{int; 2,3,5,7}; // new form

Aaron

Patrice Roy

unread,
Oct 13, 2017, 6:07:49 PM10/13/17
to std-pr...@isocpp.org
How does this idea mesh with deduction guides, which allow things like

vector v{5}; // vector<int>

?

I must admit adding a new syntax such as the one proposed here does not seem like a fruitful endeavor to me...

--
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-proposals+unsubscribe@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/966bdc02-5921-4274-8fc9-94bb468127e5%40isocpp.org.

Arthur O'Dwyer

unread,
Oct 13, 2017, 6:24:43 PM10/13/17
to ISO C++ Standard - Future Proposals
On Friday, October 13, 2017 at 2:13:56 PM UTC-7, Aaron McDaid wrote:
Instead of having to type

    std::get<1>(tup);


I would like to be able to type this instead:


std::get(1; tup);

The initial motivation is simply to make some metaprogramming look more "function-like":

is_same<T, U>{}

could become

is_same(T, U;)

It's not even any shorter!  So this is a total non-starter; please don't pursue it any further.
However, you will be very interested in studying Boost.Hana's value-based metaprogramming. In value-based metaprogramming, you write things like

    if (type<T>() == type<U>()) {
        tup[1c] = tup[2c];
    }

where "type<T>" is a tag type with an overloaded constexpr operator==, "1c" is a UDL that expands to integral_constant<int, 1>{}, and "tup" is a tuple type with an overloaded operator[](integral_constant<T, I>) template.

–Arthur

bastie...@gmail.com

unread,
Oct 14, 2017, 1:05:38 AM10/14/17
to ISO C++ Standard - Future Proposals
Le samedi 14 octobre 2017 00:24:43 UTC+2, Arthur O'Dwyer a écrit :
On Friday, October 13, 2017 at 2:13:56 PM UTC-7, Aaron McDaid wrote:
Instead of having to type

    std::get<1>(tup);


I would like to be able to type this instead:


std::get(1; tup);

The initial motivation is simply to make some metaprogramming look more "function-like":

is_same<T, U>{}

could become

is_same(T, U;)

It's not even any shorter!  So this is a total non-starter; please don't pursue it any further.

it's not totally useless, it would allow to use templated operator() that are not argument based:
auto v =  []<size_t I>() {}
v
.operator()<X>(); //only solution now
v
<5>(); //not possible to parser as '<' might be an inferior operator
v
(5;); //with that proposal

That being said that kind of expression would be difficult to parse and is not really clear.
The parsing issue is that until we find a ';' we have no way of knowing if we are parsing a params list or an argument list, that's bad.

That begin said one of the reasons why having an alternative to templated list would be interesting is the issue of the collision between inferior sign and template list introducer.
That's the reason why we have to write:
//v is a an argument of a templated function
v
.template operator()<X>(); //template keyword required here to avoid '<' to be interpreted as an inferior sign

Maybe taking a clue from D's template syntax might be interesting:
func!(42)("hello");
std
::get!(1)(t);

Why not somthing like this:
func!(42; "hello");
std
::get!(1; t);

That being said the semicolon symbol doesn't seem like a good idea to me personally.
The symbol as one meaning (delimiting declarations) and it should remain as such.

Why not use the ':' symbol instead?

func!(42: "hello");
std
::get!(1: t);

This would be simple to implement and the ':' wouldn't be necessary. 
auto v = []<size_t T>() {};
v
!(42);

As for value-based template metaprogramming it won't be that useful for long with static reflection (reflexpr, dollar sign, etc...).

Aaron McDaid

unread,
Oct 14, 2017, 4:12:48 AM10/14/17
to ISO C++ Standard - Future Proposals
Thanks Bastien for giving another example where '<' is confusing to the compiler and where a new syntax might help.

Parsing would be relatively easy in this new system - easier than what the compiler already has to do - because this uses parentheses and parentheses always come in grouped pairs in C++. '<' isn't guaranteed to have a corresponding '>'.

I'm happy to switch to a colon ':' instead of a semicolon ';'. From a parsing point of view, my understanding is they are both suitable in this proposal

You suggest that, like D, we could use something like '!' to introduce the template args. Currently, the C++ compiler has to do lookups on types and check whether a particular name is dependent before it can decide whether a given '<' might have a matching '>'. Under this proposal, the presence of a ':' would clearly demonstrate that the preceding arguments are template arguments and therefore parsing would be relatively simple, even without a '!'. This parsing could complete without any of the special semantic analysis that is currently required by C++

Thanks,
Aaron

bastie...@gmail.com

unread,
Oct 14, 2017, 8:23:18 PM10/14/17
to ISO C++ Standard - Future Proposals
On Saturday, October 14, 2017 at 10:12:48 AM UTC+2, Aaron McDaid wrote:
You suggest that, like D, we could use something like '!' to introduce the template args. Currently, the C++ compiler has to do lookups on types and check whether a particular name is dependent before it can decide whether a given '<' might have a matching '>'. Under this proposal, the presence of a ':' would clearly demonstrate that the preceding arguments are template arguments and therefore parsing would be relatively simple, even without a '!'. This parsing could complete without any of the special semantic analysis that is currently required by C++

The issue of not using something like '!' is that until we find the ';' or ':' character we don't know whether we are parsing a parameter or an argument list.
This means that you will affect negatively the performance of parsing a normal function call, as, for each function call the parser will have to try to parse a template parameter list,
check for a ':' and if either fails fallback on a argument list.
Fallbacks are the enemy of C++ parsers.
As a rule of thumb C++ parsers need to know has soon as possible what they are about to parse.
A::template func<int>(); //"template" placed before 'func' to inform the compiler that what follows is a template and to use the template-id rule instead of the normal one.


bastie...@gmail.com

unread,
Oct 14, 2017, 8:27:33 PM10/14/17
to ISO C++ Standard - Future Proposals
 Also using something akin to '!' would allow the separator to be optional is there is no arguments:

auto v = []<class T>(int = 0) {};
v
!(int);
v
!(int; 42);

inkwizyt...@gmail.com

unread,
Oct 14, 2017, 8:49:41 PM10/14/17
to ISO C++ Standard - Future Proposals
If we use `!`  then why do not use exactly same syntax as D? or at least more C++-like version with `<`?
v!<int>(); // `!<` token sequence need always `>`
v
!int(); //parsed as `(v!int)();` not `v!(int())`
v
!(int)(); //same as D


bastie...@gmail.com

unread,
Oct 15, 2017, 10:12:34 AM10/15/17
to ISO C++ Standard - Future Proposals
On Sunday, October 15, 2017 at 2:49:41 AM UTC+2, Marcin Jaczewski wrote:
If we use `!`  then why do not use exactly same syntax as D? or at least more C++-like version with `<`?
v!<int>(); // `!<` token sequence need always `>`
v
!int(); //parsed as `(v!int)();` not `v!(int())`
v
!(int)(); //same as D
Not sure if the committee would appreciate the full syntax, but the "!<" template introducer alternative is something, I believe, they might be more open to.

Ray Hamel

unread,
Oct 16, 2017, 11:27:14 AM10/16/17
to ISO C++ Standard - Future Proposals
On Saturday, October 14, 2017 at 4:12:48 AM UTC-4, Aaron McDaid wrote:
I'm happy to switch to a colon ':' instead of a semicolon ';'. From a parsing point of view, my understanding is they are both suitable in this proposal

Either semicolon or colon would conflict with statement expressions, and colon would conflict with (the narrower change of) switch-as-an-expression.

Ray

Aaron McDaid

unread,
Oct 16, 2017, 12:02:41 PM10/16/17
to ISO C++ Standard - Future Proposals
There is no conflict in either case. Can you give a practical example of any such ambiguity?

If anything, this will parsing easier for the language, especially compared to the special treatment required for '<' that is currently required.

By 'switch-as-an-expression', I guess you mean the  b ? x : y  ternary operator. There is no conflict here as the compiler can trivially see the '?' first and understand the ':'. Also, there is no context in C++ where 'b ? x' is valid, therefore there is no danger of 'foo(b ? x : y)' being misrepresented as    'foo( (b?x) : y)' and thereby rewritten as 'foo<b?x> : y'. The very simplest parsers can distinguish this.

As for semicolons, there is also no chance of a conflict with statement expressions (which are non-standard anyway). Those are introduced by '({', and are therefore clearly identifiable .


Let's go back to first principles and consider the humble opening parenthesis, '('. It has a variety of different uses in the language, and the behaviour of commas within '(' and ')' changes depending on what kind of parenthesis it is within. I'm simply proposing that, within "function-call" parentheses or braces where the comma is to separate arguments to a function (as opposed to behaving as the comma-operator, for example), that we allow a single (semi)-colon to take the place of a comma and to treat the args before the comma as template args.

Aaron

Ray Hamel

unread,
Oct 16, 2017, 1:39:02 PM10/16/17
to ISO C++ Standard - Future Proposals
I forgot that statement exprs (which are likely to be standardized) have to be enclosed in braces as well as parentheses (and, more foolishly, that switch labels are preceded with case), but there still is a possible ambiguity with semicolons (besides the GNU extension for predeclaring length arguments for VLAs): If foo names both a type and an object, whether ({foo;}) is a size-0 initializer_list<foo> or a size-1 initializer_list<decltype(foo)>.

Ray

Aaron McDaid

unread,
Oct 16, 2017, 3:00:23 PM10/16/17
to ISO C++ Standard - Future Proposals
Interesting example, but my proposal is slightly more narrow and doesn't impact on that example.

In `({foo;})`, the semicolon is attached to the braces, '{' and '}', not the parentheses, '(' and ')'. And those braces are not surrounding constructors args - if they were, there would be a type name before the '{'.

More simply, this:

    (<foo>{})

isn't valid, so it wouldn't be the equivalent of  `({foo;})`

Aaron

bastie...@gmail.com

unread,
Oct 16, 2017, 3:01:56 PM10/16/17
to ISO C++ Standard - Future Proposals
On Monday, October 16, 2017 at 7:39:02 PM UTC+2, Ray Hamel wrote:
I forgot that statement exprs (which are likely to be standardized) have to be enclosed in braces as well as parentheses (and, more foolishly, that switch labels are preceded with case), but there still is a possible ambiguity with semicolons (besides the GNU extension for predeclaring length arguments for VLAs): If foo names both a type and an object, whether ({foo;}) is a size-0 initializer_list<foo> or a size-1 initializer_list<decltype(foo)>.

Ray
I have a direct experience with GCC's lexing and parsing behaviour and I can assure you that it wouldn't be the case.
The a ':' or ';' character is a single token for the lexing phase and are used by the parser in very specific situations that wouldn't collide with existing code (ternary, label, accessibility specifier, etc...)

Jakob Riedle

unread,
Oct 17, 2017, 6:28:40 AM10/17/17
to ISO C++ Standard - Future Proposals
Hi Folks,

I have a totally different approach on this topic, which you might want to consider:

When using Templates, we are allowed to declare both Type as well as Non-Type Parameters.

This is not the case for Functions. Allowing functions to declare type parameters, as well as constexpr non-type parameters would end a long history of inconsistencies at once:


Templates were initially intended to make containers parameterizable, not to perform compile time computations or computation on types (See Stroustroup [2]).
This misinterpretation of templates as functions on types and constexpr values lead to rather clumsy constructs like std::is_reference<T>::value.
Instead of identifying this wrong conception of templates and consecutively enhancing functions to
  1. take type parameters,
  2. take constexpr non-type parameters and
  3. return types (for constexpr functions only)
the issue was partially resolved by and templated using declarations (C++11) and templated variables (C++14).
Given we can find the right regulations for these features, we would finally draw a concise line between functions on types and parameterized types.

[2] A History of C++: 1979−1991, Bjarne Stroustrup, AT&T Bell Laboratories, Murray Hill, New Jersey, 1995

Let's look at the functions you all have written in this thread that you claim are horrible to instantiate:
  • std::get<1>(tup)
    In order to return the nth value from a tuple, std::get needs this number as a parameter.
    That means - mathematically speaking - std::get is a function just like operator[](int) is one.
  • is_same<T, U>{}
    is also a function on types (mathematically speaking) and should be invoked as such:
    constexpr bool = is_same( T , U );
    However, if types were made functional at all, we should rather have a built-in operator== for them.
  • v.operator()<X>()
    same thing applies here...
So in my opinion, the root of the problem is not necessarily the uncommon '<>'-instantiation syntax but rather a deficiency as well as a misunderstanding to what is the solution to the problem.

Thinking about this feature a lot, central problems I identified are
  • Ambiguation of types and values within ordinary code:
  • constexpr typename add_pointer( typename t ){ return t*; }

    // in code
    add_pointer( int ) hello; // Is this what a variable declaration looks like? Probbaly no.
    A possible constraining of the feature would be that the results of functions returning types are only allowed to be passed to a variable of type '
    typename' or to a using declaration.
    So afterall not a big problem...
  • Linkage of such functions. This is potentially a big discussion point...
This is a very big thing (I know).
It may not solve the problems you name in the short run, because there are many things to clarify,
but it will definitely eliminate them in the long run.

I hope this gives a little help about the core of the problem.

Jakob

bastie...@gmail.com

unread,
Oct 17, 2017, 9:42:49 AM10/17/17
to ISO C++ Standard - Future Proposals
I'm completely against the idea of having a function returning a type.
You say that templated weren't thought to do computation so why should function be used to process types ?
Second, declaring the function constexpr will not help you. A constexpr function is a function that MAY be usable at compile-time.
Third this is shorter and clearer:
template<class T> using add_pointer = t*;
Fourth, a bunch of things like reflexion, meta_classes, '$' operator, etc... are better solutions to the problems you address and they don't break the meaning of a function.

That being said the committee do prefer the use of constexpr function for VALUE computation.
The current issue is not that functions can't take types as parameter, that's what template have been created for, the issue is that constexpr functions are too weak now.
The issue with std::get is not that it uses templates but that a constexpr function lose the constexpr-ness of its arguments in its body and that issue template non-type is the only recourse available to keep that constexpr-ness.
I have a proposal that responds to that issue: constexpr(true).

constexpr(true) auto operator[](size_t i) { std::get<i>(*this); } //compile-time only function, no assembly can be produced.
                                                                 
//The result and the arguments are forced to be constexpr or else compile error
std
::tuple t = {...};
t
[42];

With such a thing allowing constexpr(true) functions to take types as arguments would be a possibility as no asm is produced.
That being said having type and values argument list without distinction means that we'd lose in readability.

Ps: you miss-use std::is_same => use std::is_same<a,b>::value or std::is_same_v<a,b> not std::is_same<a, b>{}.

Arthur O'Dwyer

unread,
Oct 17, 2017, 6:54:19 PM10/17/17
to ISO C++ Standard - Future Proposals
Jakob's syntax also works (bool_constant is implicitly convertible to bool); but personally I'd use is_same_v<a,b>.

Anyway, both of you will be OH MY GOD SO INTERESTED in Hana-style heterogeneous template programming.
Here are your compelling examples rewritten in plain old C++14:


    template<class T> constexpr auto add_pointer(T) -> std::add_pointer_t<T>;

    decltype(add_pointer(int{})) hello;  // declares "int *hello"


    template<class Tuple, class Index>
    constexpr decltype(auto) operator_brackets(Tuple&& t, Index i) {
        return std::get<size_t(i)>(t);
    }

    constexpr std::tuple<int, double, char> t = {1, 3.14, 'h'};
    static_assert(operator_brackets(t, 0_zc) == 1);
    static_assert(operator_brackets(t, 1_zc) == 3.14);
    static_assert(operator_brackets(t, 2_zc) == 'h');

HTH,
–Arthur

Aaron McDaid

unread,
Oct 17, 2017, 8:42:49 PM10/17/17
to ISO C++ Standard - Future Proposals
I'll restate the initial proposal, as the last couple of comments have shown that this thread has gone off topic significantly.

Instead of writing

    make_shared<foo>(1, "two", 3.0);

I propose to be able to write

    make_shared(foo ; 1, "two", 3.0);

instead. Or perhaps, as have been suggested, we'll use `:` instead of `;`

    make_shared(foo : 1, "two", 3.0);

Slightly more clearly:

If we're in a context where commas would separate function args (as opposed to merely being the comma-operator), then we'll allow to insert template args just after the opening parenthesis with a (semi-)colon after them.

Let's clear up a few basic things first. There is no ambiguity issue. There is no valid code which would change meaning under this proposal. Yes, it is true that `:` also has meaning in the language already as part of a `case` and inside `b ? t : f` expressions, but the compiler can trivially identify those two cases easily already. This is an addition to the grammar, and not overly complex either.

This saves one character on the status quo, `(:)`, versus `<>()`. And it saves having to type `template` in some instances.

Of course, it would still involve work for compiler implementors and tools generally (like text editors). I'm not saying it's the most trivial thing ever. But it is much simpler than the `<` and `>` currently used. For example, when a current compiler sees this:

     auto x = baz(foo<bar,

it is potentially very confused. Is the `<` opening a template or a 'less-than' operator? Is the `,` separating template args for `foo` or is it separating normal arguments for `baz`?

Parentheses, `(`, and braces, `{`, (and also brackets, `[`) have the advantage that they always group in a simple way, which is why I'm relying on parentheses in my proposal. In this proposal, a comma (and `:` or `;`) is trivially associated with its enclosing parenthesis and therefore the compiler can do a lot of parsing relatively easily.

So the question is - assuming we want to change from status quo - how many characters are we willing to spend on making things easier for the compiler?

At the expense of one more character (and therefore equivalent to the status quo), we could introduce a `!`.

    make_shared!(foo : 1, "two", 3.0);

With the `!` like this, I would still like to use `:` to separate the two "kinds" of args from each other, because this still uses one fewer character than:

    make_shared!<foo >( 1, "two", 3.0); // waste of characters, why use ">(" instead of ":"


Of course, maybe nobody is interested. But if there is a problem with my proposal, I would like a more concrete example of real code where parsing would be made more difficult than current code.

Aaron

Nicol Bolas

unread,
Oct 17, 2017, 9:32:15 PM10/17/17
to ISO C++ Standard - Future Proposals
On Saturday, October 14, 2017 at 4:12:48 AM UTC-4, Aaron McDaid wrote:
Thanks Bastien for giving another example where '<' is confusing to the compiler and where a new syntax might help.

OK, so thus far, we have precisely one case where it is "necessary" to have such a syntax. And even then, it isn't really necessary so much as just more convenient than typing the actual function name.

Besides that one case, what exactly does this gain for us?

I could understand if you wanted to make parameters of a function actually be `constexpr` (that is, `get(1, some_tuple)`). But merely offering another syntax to do something we can already do quite adequately is not really helpful. What does it matter if it's `get<1>(...)` or `get(1; ...)`? You still have to remember to call it with the right syntax.

bastie...@gmail.com

unread,
Oct 18, 2017, 2:31:08 AM10/18/17
to ISO C++ Standard - Future Proposals
The there's also the use-case that in a template context the template keyword isn't required anymore. 
A::template func<int>(); //template required because name lookup not possible
A
::func!(int;);
A
::func!(int);

To Aron:
The issue of not using a '!' is that in a dependent scope or with an overloaded function/method the parser would need to pre-parse the arguments until it finds the ':' or ';' character just to parse the id of the function and then rollback the token back to the open parenthesis. If no ':' or ';' were found this would mean that it's not a template id.
This would be very costly I believe.

Igor Baidiuk

unread,
Oct 18, 2017, 3:54:35 AM10/18/17
to ISO C++ Standard - Future Proposals
Please don't. C++ has enough syntax. This one will make both reading and parsing just harder - especially taking into account that with addition of deduction rules there's even less places where one would need explicit template parameters. If you're concerned about tuples, I'd personally perfer 'tuple_var.0'

3dw...@verizon.net

unread,
Nov 30, 2017, 4:05:50 PM11/30/17
to ISO C++ Standard - Future Proposals

I think that's a waste of syntax space for no gain.

I want ADL to work for multiple argument packs:
template<typename... A, typename C, typename Real>
  ...
  hyperg(A... a, C... c, z){}..

hyperg(0.5, 1.5; -.75; z);

ADL could pick the right function.

Igor Baidiuk

unread,
Nov 30, 2017, 4:15:51 PM11/30/17
to ISO C++ Standard - Future Proposals
BTW this syntax is ambiguous - distinguish between call with template-only arguments and value-only arguments. Using multiple parens is also undecidable in case return object is callable. From my experience, there's already very little need to specify function template arguments explicitly. Even less with deduction guides. Let's not make language even more complex.

bastie...@gmail.com

unread,
Nov 30, 2017, 5:26:37 PM11/30/17
to ISO C++ Standard - Future Proposals
On Thursday, November 30, 2017 at 10:15:51 PM UTC+1, Igor Baidiuk wrote:
BTW this syntax is ambiguous - distinguish between call with template-only arguments and value-only arguments. Using multiple parens is also undecidable in case return object is callable. From my experience, there's already very little need to specify function template arguments explicitly. Even less with deduction guides. Let's not make language even more complex.
I suppose that you never had a use for std::forward then ?
Template deduction is useful, that doesn't mean that it's infallible nor that the deduction rule is the one desired nor that it covers all the desired use-cases.
Also bringing deduction guides here is useless as it relies on template deduction to produce an explicit template instantiation.

Specifying template arguments is extremely useful when you're doing metaprogramming or variadic manipulations (ex: std::make_index_sequence, passing strings as template non-type in C++20, non-type parameters in general, etc...).
Generally speaking functions/methods that requires explicit template parameters exists, they're present in many libraries and are used.
You not using them doesn't make them useless. Nor is making them less painful to use (x.template operator()<2048>(args...)).

That being said I don't particularly like the proposed idea and don't find it self-evident.

Jack Adrian Zappa

unread,
Dec 19, 2017, 10:09:01 AM12/19/17
to ISO C++ Standard - Future Proposals


On Tuesday, October 17, 2017 at 8:42:49 PM UTC-4, Aaron McDaid wrote:
I'll restate the initial proposal, as the last couple of comments have shown that this thread has gone off topic significantly.

Instead of writing

    make_shared<foo>(1, "two", 3.0);

I propose to be able to write

    make_shared(foo ; 1, "two", 3.0);

instead. Or perhaps, as have been suggested, we'll use `:` instead of `;`

    make_shared(foo : 1, "two", 3.0);

Slightly more clearly:

If we're in a context where commas would separate function args (as opposed to merely being the comma-operator), then we'll allow to insert template args just after the opening parenthesis with a (semi-)colon after them.

Let's clear up a few basic things first. There is no ambiguity issue. There is no valid code which would change meaning under this proposal. Yes, it is true that `:` also has meaning in the language already as part of a `case` and inside `b ? t : f` expressions, but the compiler can trivially identify those two cases easily already. This is an addition to the grammar, and not overly complex either.

This saves one character on the status quo, `(:)`, versus `<>()`. And it saves having to type `template` in some instances.

Of course, it would still involve work for compiler implementors and tools generally (like text editors). I'm not saying it's the most trivial thing ever. But it is much simpler than the `<` and `>` currently used. For example, when a current compiler sees this:

     auto x = baz(foo<bar,

it is potentially very confused. Is the `<` opening a template or a 'less-than' operator? Is the `,` separating template args for `foo` or is it separating normal arguments for `baz`?

Parentheses, `(`, and braces, `{`, (and also brackets, `[`) have the advantage that they always group in a simple way, which is why I'm relying on parentheses in my proposal. In this proposal, a comma (and `:` or `;`) is trivially associated with its enclosing parenthesis and therefore the compiler can do a lot of parsing relatively easily.

So the question is - assuming we want to change from status quo - how many characters are we willing to spend on making things easier for the compiler?

At the expense of one more character (and therefore equivalent to the status quo), we could introduce a `!`.

    make_shared!(foo : 1, "two", 3.0);

With the `!` like this, I would still like to use `:` to separate the two "kinds" of args from each other, because this still uses one fewer character than:

    make_shared!<foo >( 1, "two", 3.0); // waste of characters, why use ">(" instead of ":"


Of course, maybe nobody is interested. But if there is a problem with my proposal, I would like a more concrete example of real code where parsing would be made more difficult than current code.

IMHO, using the ! after the id is clearer to the reader, as is using the !<> syntax, regardless of the compiler.  I don't think that putting two orthogonal concepts in the same parameter space is a good idea from a readability perspective.

Jake Arkinstall

unread,
Dec 19, 2017, 10:20:32 AM12/19/17
to std-pr...@isocpp.org
... please don't make such a beautiful programming language so ugly.

Jack Adrian Zappa

unread,
Dec 20, 2017, 11:37:21 PM12/20/17
to ISO C++ Standard - Future Proposals
On Tuesday, December 19, 2017 at 10:20:32 AM UTC-5, Jake Arkinstall wrote:
... please don't make such a beautiful programming language so ugly.

Ugly?  Like having the word template required so as to disambiguate a template from a comparison?

Marius Bancila

unread,
Dec 21, 2017, 1:56:42 AM12/21/17
to std-pr...@isocpp.org
I don't see any benefit of this except for some rare edge cases (that have been described above). You want to have two different syntax for the same thing. It's not shorter, it's not clearer. BTW, when I saw this:

make_shared(foo : 1, "two", 3.0);

the first thing that came to my mind was named parameters, as it is in C#, and not templates. 

I's say we should keep it simple for everybody by not introducing this.

--
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-proposals+unsubscribe@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/05334640-3e48-4b67-aee8-a511ec6d62a0%40isocpp.org.

Jake Arkinstall

unread,
Dec 21, 2017, 12:56:28 PM12/21/17
to std-pr...@isocpp.org
Extremely ugly. At the moment there is an extremely clear easy-to-spot distinction between templates and parameters. This is great because they are completely separate things with completely different behaviour and completely different meaning. In longer function declarations you can do some nice indentation.

The proposal is to bundle the two together in two lists separated by a symbol that looks very similar to a comma. I'm not saying this is going to be a root cause of eye strain amongst C++ developers, but this makes the language more awkward to LOOK at, which is something developers tend to do a lot. Indentation becomes a nightmare.

It will require some really funky code to refer to types unless we want two versions of the notation flying around. I don't even want to imagine the circle of hell the proposal's impact on functions pointers/std::functions would belong in. How does the compiler or even the reader tell the difference between a reference to a function and a call of it if we place everything in parentheses? Especially if the parameters have defaults.

What we have is a major piece of ugly code in very specific situations where a template (or, should I say, the use of operator<X>()) doesn't even make sense in the first place. Operator overloads are a nice convenience. Templates are a nice convenience. Put the two together mindlessly and you're asking for trouble, but that's the nature of the operator conveniences and always has been.

So yes, this proposal - and the suggested modifications to it - are ugly, confusing, and even potentially discriminatory to those with minor vision problems (... or dead pixels).

--
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-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Dec 21, 2017, 1:14:22 PM12/21/17
to ISO C++ Standard - Future Proposals
On Thursday, December 21, 2017 at 12:56:28 PM UTC-5, Jake Arkinstall wrote:
Extremely ugly. At the moment there is an extremely clear easy-to-spot distinction between templates and parameters. This is great because they are completely separate things with completely different behaviour and completely different meaning. In longer function declarations you can do some nice indentation.

The proposal is to bundle the two together in two lists separated by a symbol that looks very similar to a comma. I'm not saying this is going to be a root cause of eye strain amongst C++ developers, but this makes the language more awkward to LOOK at, which is something developers tend to do a lot. Indentation becomes a nightmare.

It will require some really funky code to refer to types unless we want two versions of the notation flying around. I don't even want to imagine the circle of hell the proposal's impact on functions pointers/std::functions would belong in. How does the compiler or even the reader tell the difference between a reference to a function and a call of it if we place everything in parentheses? Especially if the parameters have defaults.

What we have is a major piece of ugly code in very specific situations where a template (or, should I say, the use of operator<X>()) doesn't even make sense in the first place. Operator overloads are a nice convenience. Templates are a nice convenience. Put the two together mindlessly and you're asking for trouble, but that's the nature of the operator conveniences and always has been.

So yes, this proposal - and the suggested modifications to it - are ugly, confusing, and even potentially discriminatory to those with minor vision problems (... or dead pixels).

Here's the thing though. As C++ has developed, the distinction between template parameters and non-template parameters has become increasingly blurred. It began in C++98/03, when we got function template argument deduction: the ability to deduce template parameters from non-template parameters. In most cases, there were deducing type template parameters.

C++11 gave us constexpr functions. With the right tools, this allows us to provide non-type template parameters through deduction:

template<size_t sz>
void foo(std::integral_constant<sz>)
{
 
//Use sz, not the function parameter.
}

foo
(10_const);

Where `_const` is a UDL that generates an `integral_constant`.

The OP's feature is essentially asking us to be able to do away with the pretense. To get rid of the `_const` noise and be able to treat template arguments like function arguments. The use of a semicolon is merely to appease the syntactic needs of C++, since there still is a distinction between the two.

But as that distinction breaks down, the need for this feature increases.

Personally, `constexpr` parameters make far more sense than doing it with this syntactic distinction.

Arthur O'Dwyer

unread,
Dec 21, 2017, 1:47:38 PM12/21/17
to ISO C++ Standard - Future Proposals
On Thu, Dec 21, 2017 at 10:14 AM, Nicol Bolas <jmck...@gmail.com> wrote:
On Thursday, December 21, 2017 at 12:56:28 PM UTC-5, Jake Arkinstall wrote:
Extremely ugly. At the moment there is an extremely clear easy-to-spot distinction between templates and parameters. This is great because they are completely separate things with completely different behaviour and completely different meaning. In longer function declarations you can do some nice indentation.
The proposal is to bundle the two together in two lists separated by a symbol that looks very similar to a comma. [...]
It will require some really funky code to refer to types unless we want two versions of the notation flying around. [...]

Here's the thing though. As C++ has developed, the distinction between template parameters and non-template parameters has become increasingly blurred. [...]
The OP's feature is essentially asking us to be able to do away with the pretense. To get rid of the `_const` noise and be able to treat template arguments like function arguments. The use of a semicolon is merely to appease the syntactic needs of C++, since there still is a distinction between the two.
But as that distinction breaks down, the need for this feature increases.

Personally, `constexpr` parameters make far more sense than doing it with this syntactic distinction.

Well, what the heck, I'm subscribed to this thread already... :P

We see three fundamental points of view in this thread:
(1) Aaron and Jack say "I don't like the current syntax of templates; let's invent a new syntax."
(2) Jake, Marius, Igor, etc., say "I don't like your new syntax; let's keep the current syntax."
(3) Nicol says "I don't like the current semantics of templates. Let's invent the new semantics first, and then worry about whether we need any new syntax to express it."

The reason you don't see any regulars (until Nicol just now) weighing in in this thread is that this thread is not proposing any actual new feature of C++; it's just proposing a sort of "preprocessor" that could transform "misplaced semicolons" into angly-brackety syntax. If I wanted such a thing, I could probably take a day and write it as a Perl script. (Hyperbole. Regexes can't brace-match, and I would use Python anyway. ;))

The time to bikeshed the syntax of a feature is (perhaps unfortunately) when that feature is being standardized, not literally 20 years later.  Jack's bikeshedding is uninteresting precisely because it is so obviously irrelevant to the real-world standardization process.

Nicol's discussion topic — what are the fundamental distinctions between template parameters and runtime parameters, and can we erase some of those distinctions? — is (comparatively) far more interesting because it has the potential to generate new ideas that might inform the future direction of C++. Personally I'm bearish on "constexpr parameters"; I actually don't see much blurring of compile-time and runtime at the conceptual level in C++, just at the syntactic level. Analogously, ever since C++98 we have seen a "blurring" of the distinction between integral types and floating-point types; I can easily write code that works (more or less) for both kinds of types. Templates happen to be the mechanism in both cases. But that's just because templates are C++'s one tool for producing several slightly different compilations of a single lexical chunk of code.

By the way, Amdahl's Law suggests that if you really want to trim the fat or relieve the pain of C++, you should start by figuring out where most of the fat and the pain actually are.  Even if we limit ourselves to caring only about the verbosity and visual "noise" of the 20-year-old template syntax, why is Aaron looking at explicitly specified template arguments (which rarely appear in idiomatic C++11 code, and even less in C++17), instead of looking at template declarations?  

    template<class T, class U>
    struct X {
        template<class V> auto process(V) -> V;
    };
    template<class T, class U>
    template<class V>
    auto X<T,U>::process(V v) -> V {
    }

(A decent answer is, "Because he's already aware of terse declaration syntax for Concepts and is supporting that proposal." I'm ambivalent about terse syntax myself, but I did recently realize that perhaps it would neatly solve a problem I've had in one of my pet projects, which is to produce both a "classical-OO" and a "generic-programming" version of an API from a single source file. If I can make `void play(Bot&)` either a function-taking-a-polymorphic-class or a function-template-with-a-constraint simply by #ifdeffing in a new definition for the name `Bot`, then I might be able to quickly switch between the two implementations without lots of tedious #ifdeffing. However, I'm not sure that this will actually work in practice, because `play` is a member function of a class analogous to `X` above, so I'd still need one level of `template<class T, class U> ... X<T,U>::...` noise anyway.)

–Arthur

Jake Arkinstall

unread,
Dec 21, 2017, 2:15:27 PM12/21/17
to std-pr...@isocpp.org


On 21 Dec 2017 18:14, "Nicol Bolas" <jmck...@gmail.com> wrote:
Here's the thing though. As C++ has developed, the distinction between template parameters and non-template parameters has become increasingly blurred. It began in C++98/03, when we got function template argument deduction: the ability to deduce template parameters from non-template parameters. In most cases, there were deducing type template parameters.

C++11 gave us constexpr functions. With the right tools, this allows us to provide non-type template parameters through deduction:

template<size_t sz>
void foo(std::integral_constant<sz>)
{
 
//Use sz, not the function parameter.
}

foo
(10_const);

Where `_const` is a UDL that generates an `integral_constant`.

The OP's feature is essentially asking us to be able to do away with the pretense. To get rid of the `_const` noise and be able to treat template arguments like function arguments. The use of a semicolon is merely to appease the syntactic needs of C++, since there still is a distinction between the two.

But as that distinction breaks down, the need for this feature increases.

Personally, `constexpr` parameters make far more sense than doing it with this syntactic distinction.

This makes sense to some extent, and partially knocks down one of my two main concerns (that the template parameters and function parameters are two different concepts - the other is syntax, as suggestions thus far are the textual equivalent to green text on a red background).

The remaining concerns I have about the template parameter / function parameter distinction are as follows.

First of all, contexpr functions are only useful in this case for inputs known at compile time. We can never keep the benefits of templates whilst also allowing general arguments in. What is the workaround - generating template versions of the functions and non-template versions? Will it not simply cause more confusion if we have to explain to people why the first half of a function call can not be some constexpr function evaluated on post-compilation input? To me, the contexpr is a step in a very useful direction, but I don't see this being the logical outcome.

Secondly, what does this say about types as template parameters, which are somewhat less flexible than values?

Thirdly, are we restricting this to mere function calls or are we talking about the possibility of declarations too? Because I have no idea how sfinae is supposed to be handled here, and we would need to still have the ability to use templates as they are for backwards compatibility - and two syntaxes for the same thing smells familiar *cough* trigraphs *cough*

What I propose is what I think should always have been done for trigraphs. Have a preprocessing program that parses the proposed style into current C++ code to be input to the compiler. We're talking about a handful of regular expressions, perhaps using the long form of function calls every time or just when necessary. Allow code to be written in this way but still have it easy to convert and utilise by everyone else for now. Once there exists a community of people comfortable using it, it would be a lot easier to get by the standards committee, in the same vein as Boost.

Nicol Bolas

unread,
Dec 21, 2017, 5:01:46 PM12/21/17
to ISO C++ Standard - Future Proposals
On Thursday, December 21, 2017 at 2:15:27 PM UTC-5, Jake Arkinstall wrote:
On 21 Dec 2017 18:14, "Nicol Bolas" <jmck...@gmail.com> wrote:
Here's the thing though. As C++ has developed, the distinction between template parameters and non-template parameters has become increasingly blurred. It began in C++98/03, when we got function template argument deduction: the ability to deduce template parameters from non-template parameters. In most cases, there were deducing type template parameters.

C++11 gave us constexpr functions. With the right tools, this allows us to provide non-type template parameters through deduction:

template<size_t sz>
void foo(std::integral_constant<sz>)
{
 
//Use sz, not the function parameter.
}

foo
(10_const);

Where `_const` is a UDL that generates an `integral_constant`.

The OP's feature is essentially asking us to be able to do away with the pretense. To get rid of the `_const` noise and be able to treat template arguments like function arguments. The use of a semicolon is merely to appease the syntactic needs of C++, since there still is a distinction between the two.

But as that distinction breaks down, the need for this feature increases.

Personally, `constexpr` parameters make far more sense than doing it with this syntactic distinction.

This makes sense to some extent, and partially knocks down one of my two main concerns (that the template parameters and function parameters are two different concepts - the other is syntax, as suggestions thus far are the textual equivalent to green text on a red background).

The remaining concerns I have about the template parameter / function parameter distinction are as follows.

First of all, contexpr functions are only useful in this case for inputs known at compile time. We can never keep the benefits of templates whilst also allowing general arguments in.

... why not?

I understand that there are issues to work out. Consider a `constexpr` parameter version of tuple's `get`:

template<typename ...Args>
constexpr auto &get(constexpr size_t ix, tuple<Args...> &tpl)
{
 
constexpr auto offset = tuple<Args...>::_GetOffset(ix); //Guaranteed constexpr call.
 
void *ptr = static_cast<tuple_element_t<tuple<Args...>, ix>(tpl._GetPtrFromOffset(offset));
}

The first statement, and the `tuple_element_t` part, have to be evaluated at compile time. The rest of the function gets evaluated at runtime.

Our current tool for this kind of split processing is to directly split it through metaprogramming. The metaprogramming is the compile-time part, and the non-metaprogramming is the runtime part.

But who is to say that it has to be that way? Why can't the compiler figure out which expressions, types, and whatever can be executed at compile time and runtime, doing the breaking out automatically?

Remember: it was not that long ago that people thought that `constexpr` couldn't be done in C++. Now, we're genuinely talking about allocating memory at compile time (which, honestly, scares me a lot more than the above). Whose to say that the above combination of partial compile-time evaluation and partial runtime evaluation is impossible?

What is the workaround - generating template versions of the functions and non-template versions? Will it not simply cause more confusion if we have to explain to people why the first half of a function call can not be some constexpr function evaluated on post-compilation input?

If I do `constexpr int x = ...;` then I'm making a hard statement that whatever ... is must be a compile-time constant. Why would a user think `void foo(constexpr int x)` would behave any differently?

A function with `constexpr` parameters is a function that cannot be called with runtime values for those parameters. There's nothing to "workaround" here; there's no need to "generate non-template versions" or whatever, since you cannot provide non-`constexpr` values for those parameters.

To me, the contexpr is a step in a very useful direction, but I don't see this being the logical outcome.

Secondly, what does this say about types as template parameters, which are somewhat less flexible than values?

Well, that rather depends on how you think about it. Template metaprogramming does a lot of things that it really shouldn't have to. In many cases, we use it not because it's reasonable syntax that naturally expresses our purpose, but because it's literally the only way to do the job.

By empowering `constexpr`, we give ourselves more natural ways to express what we used to do through metaprogramming. It's similar to how we can technically do through SFINAE what most of concepts gives us, but concepts provides an actual language feature that greatly lowers the bar for users understanding of what's going on. We have something similar for metaclasses and reflection and the like.

Thirdly, are we restricting this to mere function calls or are we talking about the possibility of declarations too?

Restricting what, precisely? A `constexpr` function parameter is a `constexpr` function parameter. It would naturally have to be part of the function's type.

Is such a function a template function? I don't know; does it really have to be? I'm not sure that it does. Do `constexpr` functions have to be template functions? No. But that's where the idea for `constexpr` functions came from originally; people using metaprogramming to generate values.

But we didn't see the need to declare that a `constexpr` function itself was a template function. Similarly, I'm not sure that we need to say that a `constexpr` function with a `constexpr` parameter is a template function. That doesn't mean we certainly don't. But it's not something we should assume.

Because I have no idea how sfinae is supposed to be handled here, and we would need to still have the ability to use templates as they are for backwards compatibility - and two syntaxes for the same thing smells familiar *cough* trigraphs *cough*

What I propose is what I think should always have been done for trigraphs. Have a preprocessing program that parses the proposed style into current C++ code to be input to the compiler. We're talking about a handful of regular expressions, perhaps using the long form of function calls every time or just when necessary. Allow code to be written in this way but still have it easy to convert and utilise by everyone else for now. Once there exists a community of people comfortable using it, it would be a lot easier to get by the standards committee, in the same vein as Boost.

But that means that you are restricted to expressing only what you can currently express in C++. There's no need for that restriction when dealing with a new language feature.

And besides, I think it'd be easier to implement the feature into a branch of Clang than to make a preprocess step.

Richard Hodges

unread,
Dec 22, 2017, 4:56:03 AM12/22/17
to std-pr...@isocpp.org
I have to say that to me, the proposed syntax:

foo(foo : 1, bar : 2)

looks alarmingly like passing an associative array of named arguments. Is that the intent?

The idea of constexpr arguments sounds appealing, particularly now that we have if constexpr.

That sounds like it could be the beginning of being able to express compile-time logic that is actually legible.

Has this been proposed?



 

--
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-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Reply all
Reply to author
Forward
0 new messages