Reputation: 6798
Given the below program, GCC (g++
, but I presume the non-std::
bits apply to C too), as can be seen on IDEone.com, behaves as follows:
const
qualification, which is honoured.const
or lack thereof of the first variable is used for all variables declared in that line. Any further const
keywords produce a syntax error.My questions are therefore:
I have searched but had no luck. I do have n3797.pdf open in front of me here, but I just don't seem to be at the required level to understand its specification of the grammar yet. :( So hopefully someone who is can assess and translate as required.
Please don't just say not to declare multiple variables in one line. I know why it's discouraged, and I try not to. FWIW, for
loops make it tempting as a way to get a const
end iterator - which seems possible if said iterator is a pointer, but not if it's a class. Anyway, I'm not asking for code review. The topic is why this difference exists, independent of how advisable it is to exploit it.
#include <iostream>
#include <type_traits>
int main()
{
int i{42};
int *p1 = &i, *const p2 = &i; // OK
std::cout << std::is_const< decltype(p1) >::value << '\n'; // 0
std::cout << std::is_const< decltype(p2) >::value << '\n'; // 1
int *const p3 = &i, *p4 = &i; // OK
std::cout << std::is_const< decltype(p3) >::value << '\n'; // 1
std::cout << std::is_const< decltype(p4) >::value << '\n'; // 0
int const l = i, m = i; // OK
std::cout << std::is_const< decltype(l) >::value << '\n'; // 1
std::cout << std::is_const< decltype(m) >::value << '\n'; // 1
//int j = i, const k = i; // error: expected unqualified-id before ‘const’
}
Upvotes: 1
Views: 706
Reputation: 6798
As @JonathanLeffler pointed out:
Chapter 8 of n3797.pdf (p180-181) shows that this is OK in the grammar rules there.
Each element of a list of declarators can be either a noptr-declarator or a ptr-declarator. The ptr-operator component of the ptr-declarator case has its own cv-qualifier-seq opt. But the noptr-declarator doesn't.
So much for the legalese, but why is it like this? Well, once I started to think about this more, it made sense: There is a distinct split between the leading typename and the list of declarators. Non-pointer declarators create variables with that exact type, which hence get the same const
ness as it.
In contrast, pointer declarators also have their own const
ness, separate from that of the referred type. And I think the reason each pointer in a list of declarators can have a distinct const
ness is due to the ((in)famous) way that the ptr-operator binds: to variable names, not to the referred variable type.
So for code like this to ever be seen as 'equivalent' to non-pointer declarations and mean 'all of these pointers are themselves const'...
int* const p = &i, q = &j, r = &l;
...then the grammar would have to be totally different and consider the ptr-operator as part of the typename, not as an attribute of each variable.
But as we know, it's the latter that's true. So, given that, the presence of a cv-qualifier-seq opt in each ptr-operator component is the only way to let even one pointer have its own const
ness, separate from that of the referred-to type. The fact that each other pointer in a list of declarators can have a distinct const
ness is just a side-effect of that, since each declarator needs its own ptr-operator (to avoid becoming a non-pointer).
This all seems very obvious in hindsight. I guess I'm just having a slow day.
Upvotes: 1