Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declarator modifier position in variadic templates

Declarators. Yes, declarators. They are the source of a lot of coding convention debates. It's a really good topic for arguments -- C++ doesn't rule which one is better than the other (it doesn't care!). And so we can write these without worrying someone might make fun of you:

int x;

int& a = x;
int &b = x;

int* c = &x;
int *d = &x;

In syntactical terms, b and d are "more valid" than the others. We are required to put declarator modifiers before the names:

int m, *n;   // m is an int; n is a pointer to int

But the tide seems to turned in favor of one. With C++11's variadic templates, the position of declarators seems to be restricted to the form where the modifiers are closer to the base type:

template<typename... Ts>
void VariadicFuncRef(const Ts&... args) { }
                             ^
template<typename... Ts>
void VariadicFuncPtr(Ts*... args) { }
                       ^

It is an error to write these forms:

template<typename... Ts>
void VariadicFuncRef(const Ts... &args) { }

template<typename... Ts>
void VariadicFuncPtr(Ts... *args) { }

For a concrete example, click here.

And so, my question is this: Why are we limited to this (the legal) form and can't use the other?

Any thoughts guys?

ADDITIONAL 1: I'm also interested on the design decision on why is this "rule" "enforced".

like image 246
Mark Garcia Avatar asked Dec 19 '12 07:12

Mark Garcia


3 Answers

The old declarator syntax was simply one rule with insignificant white space, but your idea is two. I doubt the committee would be in favor of having two rules when one would be sufficient.

The choice of putting the declarator on the left was most likely made for two reasons:

  • idiomatic C++ nowadays prefers int* x rather than int *x (Bjarne uses the former for example). Most consider declarator syntax a mistake.
  • Parameter pack expansion expands things to the left of the ..., not to the right.
like image 186
Pubby Avatar answered Nov 05 '22 19:11

Pubby


First of all, I would not say that int &a is more valid than int& a. You may say it is more readable, or a good practice, but that is a different thing altogether.

Second, the parameter unpack syntax T... can be read as "the type-pattern on the left side of ... is expanded, forming same or different actual function parameter types matching the form of the type-pattern". So when you write f(T&...), it can be expanded to f(int&, float&, double&, Xyz&, Abc&). But that doesn't seem to be so obvious (at least to me) when the syntax is T...&. So maybe, that is one of the several other possible reasons why the Standard made it like that.

Also note that args in T&...args is optional. So if you don't write it, then void f(T...&) looks weird (to me, at least) and void f(T&...) looks better aesthetically at least.

You could also compare syntaxes like : T * const & ... with T... * const &. The type-pattern is more obvious in the former syntax than in the latter.

like image 3
Nawaz Avatar answered Nov 05 '22 19:11

Nawaz


Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

-- Antoine de Saint-Exupery

The difficulty in programming language design is not to add features, it is to refrain from adding them. Every feature, every variation of syntax, has a cost:

  • they must be specifically coded in the compiler
  • they pose the risk of introducing bugs
  • they pose the risk of interacting badly with another feature/variation
  • ...

The syntax in C and C++ is (mostly) whitespace insensitive, therefore it does not matter where you place the & or * and both preferences can coexist without extra burden; however in the case of variadic templates it does matter because of the ... token. The comittee thus decided to pick one, and they picked the most idiomatic in C++ (which put emphasis on the full type rather than the base type).

like image 2
Matthieu M. Avatar answered Nov 05 '22 19:11

Matthieu M.