Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Choose whether to put a space on the left or right of `...` and apply consistently throughout library #430

Closed
StephanTLavavej opened this Issue Jan 21, 2015 · 6 comments

Comments

Projects
None yet
6 participants
@StephanTLavavej
Copy link

StephanTLavavej commented Jan 21, 2015

Search N4296 for class ...

There are a couple dozen occurrences of class ... Meow in Core, with similar function parameter packs. That's a questionable style, but not outright toxic.

However, there are 4 occurrences of class ...Args in Library, which is yuck and bad and wrong. They are:

30.3.1 [thread.thread.class]:
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

30.3.1.2 [thread.thread.constr]/3:
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

30.4 [thread.mutex]:

template<class Callable, class ...Args>
  void call_once(once_flag& flag, Callable&& func, Args&&... args);

30.4.4.2 [thread.once.callonce]:

template<class Callable, class ...Args>
  void call_once(once_flag& flag, Callable&& func, Args&&... args);
@zygoloid

This comment has been minimized.

Copy link
Member

zygoloid commented Jan 21, 2015

However, there are 4 occurrences of class ...Args in Library, which is yuck and bad and wrong.

I'm curious what your argument is here; is it just aesthetic, or is there some more fundamental reason?

... consistently binds to the thing on its right, so space on the left is the style most consistent with the grammar. That said, the same argument suggests that *, &, and && should have the space on the left, and we don't do that, largely because some prefer to think of the type being "the thing on the left of the name" rather than "the thing surrounding the name". But the ... is not part of the type; that argument doesn't apply here.

[My general feeling is that the formatting of code samples in the C++ standard should reflect the C++ language as well as possible. To this end, the core language deliberately uses a mixture of styles to emphasize that whitespace doesn't matter. The library specification lives in a somewhat different world; it should stick to a single consistent style. All else being equal, I think it would make sense for that style to match the C++ grammar (rather than only working in common but special cases -- one declarator per declaration, no postfix declarators, no paren declarators), because that would improve the internal consistency of the standard.]

@zygoloid zygoloid changed the title "class ...Args" puts the ellipsis on the wrong side Choose whether to put a space on the left or right of `...` and apply consistently throughout library Jan 21, 2015

@StephanTLavavej

This comment has been minimized.

Copy link
Author

StephanTLavavej commented Jan 22, 2015

It's purely an aesthetic argument. In the Library, we think of typename... meaning "lots of types", regardless of how the grammar is technically formed.

I see your point about Core mixing styles, and I don't think that it needs to change. If you are tempted to change Library to say class ...Args (which would make me regret mentioning this!), please ask the LWG reflector first, as I suspect there will be wide agreement that class... Args is preferable. We Library implementers have to read those clauses, after all. :->

@jwakely

This comment has been minimized.

Copy link
Member

jwakely commented Jan 22, 2015

My preference for the library would be class... Args (but only for aesthetic reasons, I can't copy&paste template declarations from the standard to libstdc++ anyway because we use typename not class)

@oblitum

This comment has been minimized.

Copy link
Contributor

oblitum commented Jan 22, 2015

I vote for grammar. Standand's wording should honor it (so we all don't forget there's no such thing as the type is the thing on the left) and leave aesthetics as a deciding factor over the grammar to our own codebases.

I completely agree with @zygoloid in his two points:

  • To emphasize that space doesn't matter, not caring much about it if appropriate.
  • Formatting of code samples in the C++ standard should reflect the C++ language as well as possible.

I recall a recent well known book, by known author, by known reviewers, which contained const char[3]&. This makes me have the contrary position of @StephanTLavavej. This general trend of binding to the left is what I see as "yuck and bad and wrong".

@mclow

This comment has been minimized.

Copy link
Contributor

mclow commented Jan 22, 2015

My opinion is that being consistent (to help searching) is the goal.
Which option is chosen (though I prefer "class.... Blah") is less important (to me).

@FrankHB

This comment has been minimized.

Copy link
Contributor

FrankHB commented Jan 25, 2015

I personally strongly prefer class... Args. I don't think it is merely about aesthetic.

This stuff of syntax problem is clearly a clone of declarators/type-names from the C language, i.e. T* x vs. T *x. The ... is just like *(a major component of pointer in ISO C, ptr-operator in ISO C++). This is even more complicated.

Note there are two different kinds of use cases.

  • To denote an identifier being declared within a sequence of possibly multiple declarators is pointer-like, e.g. T* p; vs. T *p;.
  • To express the one type is pointer-like under other circumstances, e.g. (T*)x vs. (T *)x, or int foo(X*, Y*); vs. int foo(X *, Y *);.

Since C++ allows creating new types only in declarations, they are even more explicit and easier to be differentiated.

Well, I know it is sometimes reasonable to keep these punctuations (*, &, etc) on the right side:

T *p, *q; // OK, both p and q are of same type.
T* x, y; // WTF... why x and y are of different types?

But this is certainly not fit for all cases. If there is no chance to be ambiguous, why I must care to avoid it?

int foo(X* x, Y* y); // OK.
int foo(X* x, y); // Certainly ill-formed. Why adding the artifact separator?

Yes, they are inconsistent. However, this is the original design of C, and it would probably never be changed by C++ (damn compatibility ...).

So don't cheat newbies, let them be as-is.

One can also easily make convention to allow only one style:

T* x; // Clear (esp. in a class defenitions. Friendly to document (per entity).
T* y; // Only one declarator per declaration. No ambiguity. Good.

or

T *x;
T *y;

or even

T * x;
T * y;

I think the first is enough.

Moreover, are there any reasons to think things like static_cast<T *> always better than static_cast<T*> beyond the (hollow) consistency above?

Nope.

Now pay attention to class... vs. class .... It is simpler, just similiar like a function parameter-declaration-list, rather than a simple-declaration with multiple declarators.

We can't get the ambiguity just by put ... at any side:

template<class T, S>
class C;
template<class T, ...S>
class D;
template<class... T, S>
class E;
// All ill-formed.

template<class ...T, class S> may be well-formed sometimes. Here S is an identifier, but what does ...T mean?

Here comes the reason I've found to support my point: the formal syntax.

A declarator indirectly reuses the construct of a declarator-id like ... identifier. However, there is no similar construct in template-parameter-list. An optional ... in type-parameter is placed parallel with both left-side type-parameter-key and right-side identifier.

If there are no other rules to force the use of class ...T, why add a redundant space character before ..., and eliminate the space after it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.