underscore_d
underscore_d

Reputation: 6798

GCC allows 1-line declaring of multiple pointers, but not non-pointers, with different const qualifiers

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:

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

Answers (1)

underscore_d
underscore_d

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 constness as it.

In contrast, pointer declarators also have their own constness, separate from that of the referred type. And I think the reason each pointer in a list of declarators can have a distinct constness 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 constness, separate from that of the referred-to type. The fact that each other pointer in a list of declarators can have a distinct constness 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

Related Questions