Constant qualifier pros/cons, type const& versus const type&, etc.

P

paulo

Hi,
I've always been trying to search for the best coding standard.
By now I understand that every project/person has its own favorite way
of laying out their code.
Well, I'm ok with most of it, but there's one part that I think goes
beyond pure coding stile. Or maybe not...

I'm talking about the constant qualifier.
My question can be stated as "where can I read about the best
practices for the const qualifier usage"?

For example. When I learned c++ I was thought to use:
class A {};

class B {
public:
//...
B const& attributeName() const;
void attributeName(B const& b) const;
private:
B attribute_name_;
};

I do think it's a good thing to always make constant something that
shouldn't be modified.
So, whenever that situation comes about I write:
SomeType const t(some_initialization_parameter);
//don't want t to be modified from here on...

Sometimes this is actually complicated, as in for loops: (same thing
for iterators)
for(vector<int>::size_type i(0), end(_vector.size()); i != end; ++i)
//here I could modify both i and end. Not good!

I would like to be able to write
for(vector<int>::size_type i(0), const end(_vector.size()); i != end; +
+i)
//sometimes it would be good to write here
vector<int>::size_type const i' = i;

Well, if I declare/initialize "end" before the loop this problem will
disappear, but
I'll pollute the scope with that name.
As for the i', there's no way to guarantee that i would not be
modified anyway.
Any good/elegant idea on how to do this?

Actually I'm just being a bit psychotic, trying to make the compiler
spot all my possible missuses of the language.


Another thing is the point that I fighting with right now.
Consider the following code:

void procedure(A const& a);

is this preferable to, for example:

void procedure(const A &a) const;

And why?


What about the pointer (or basic type) case. Allot o people will
write:

void someProcedure(const B* pb);

Completely different from:

void someProcedure(B const *const pb);

or

void someProcedure(const B* const pb);

Which is not as pretty but is arguably safer.

I would just like to hear/read some opinions about this subject.
I think it might not be considered very important, apart from this
last example, but if some one cares about it, please I would like to
know your opinion.

Thank you.
 
A

abhay.burli

Hi,
I've always been trying to search for the best coding standard.
By now I understand that every project/person has its own favorite way
of laying out their code.
Well, I'm ok with most of it, but there's one part that I think goes
beyond pure coding stile. Or maybe not...

I'm talking about the constant qualifier.
My question can be stated as "where can I read about the best
practices for the const qualifier usage"?
[...]
Thank you.

Abt placement of const qualifier in code, read what the language
inventor has to say @ http://www.research.att.com/~bs/bs_faq2.html#constplacement

IMHO, it is safer to place const after the type, to avoid typedef
pitfalls.
Ex: typedef char * pchar;
now you declare mystrcmp(const pchar, const pchar);
this actually is mystrcmp(char * const , char * const); instead of the
intended const char * in mystrcmp parameters.
 
J

Juha Nieminen

IMHO, it is safer to place const after the type, to avoid typedef
pitfalls.
Ex: typedef char * pchar;
now you declare mystrcmp(const pchar, const pchar);
this actually is mystrcmp(char * const , char * const); instead of the
intended const char * in mystrcmp parameters.

Exactly how does "const after the type" solve the problem? If you
write: mystrcmp(pchar const, pchar const), its meaning is identical to
the above.
 
J

Juha Nieminen

paulo said:
I do think it's a good thing to always make constant something that
shouldn't be modified.

I have self-learned the same habit.
Sometimes this is actually complicated, as in for loops: (same thing
for iterators)
for(vector<int>::size_type i(0), end(_vector.size()); i != end; ++i)
//here I could modify both i and end. Not good!

Well, there are things which the language syntax just doesn't support,
so you'll just have to live with them.

You could put the loop body into its own function and pass the loop
index as a const parameter, but that's often more trouble than it's
worth (especially if the loop body needs access to variables in the same
scope as the loop itself).
 
J

James Kanze

Exactly how does "const after the type" solve the problem? If
you write: mystrcmp(pchar const, pchar const), its meaning is
identical to the above.

The problem is that people tend to read the typedef'ed
declaration as if it were a macro, interpreting "const pchar" as
"const char*", and "pchar const" as "char * const".

More generally, there are cases where the const must come after
what it modifies, and there are cases where it can go either
before or after. Since you have to put it after in some cases,
coherence argues for doing so everywhere.

More generally: in the declaration specifier part of a
declaration, order plays no role: you can write things like
int const long static unsigned i ;
and it's legal. I'd argue that a good coding standard will
define an order. Two more or less "invariants" I've seen are:

-- The storage specifier (static, extern, etc.) is always
first. (I think C99 even deprecates putting it elsewere.)

-- Direct type modifiers (like long or unsigned) always precede
the base type, if the base type is present, and the
signedness modifier precedes the size modifier. In other
words: "long int" instead of "int long", and "unsigned char"
instead of "char unsigned". Also, no other elements are
interposed---I've never seen anything like "long const int",
for example.

The rule for const in this context varies: on one hand, it's
also a "type modifier" of sorts, so should come in front; on the
other, it must come after in contexts outside of the declaration
specifier, so consistency says put it after here as well. I
personally prefer after, but I'll go along with the local coding
conventions---it's one of those things that really aren't worth
arguing about.
 
A

abhay.burli

Juha ..@ .. wrote
Actually, i wanted to tell that const binds to the right of the
type in some situations like this, so it will help if one is in the
habit
of using it after the type.

Another reason is as explained by Josuttis in his book: C++ Templates:
The Complete Guide

It make two points, in support of using const after a type, rather
than before it (that is int const, rather than const int):

This way, what is constant is always in front of the const qualifier
(such as int const and int * const) so that textually substituting a
typedef with its type gives the same type

Ex.
The last two lines mean same here ...

typedef int * PINT;
typedef PINT const CPINT;
typedef int * const CPINT;


Whereas in the following, it means something different:

typedef int * PINT;
typedef const PINT CPINT;
typedef const int * PCINT;

Yes, there is no point in debating this. As JK said this issue is
subjective and is more of a matter of one's taste. Better to follow
the local coding guidelines.

Regards,
Abhay
 
J

Juha Nieminen

James said:
-- The storage specifier (static, extern, etc.) is always
first. (I think C99 even deprecates putting it elsewere.)

-- Direct type modifiers (like long or unsigned) always precede
the base type, if the base type is present, and the
signedness modifier precedes the size modifier. In other
words: "long int" instead of "int long", and "unsigned char"
instead of "char unsigned". Also, no other elements are
interposed---I've never seen anything like "long const int",
for example.

What about things like volatile and mutable?
 
B

Bo Persson

Juha ..@ .. wrote

Actually, i wanted to tell that const binds to the right of the
type in some situations like this, so it will help if one is in the
habit
of using it after the type.


This way, what is constant is always in front of the const qualifier
(such as int const and int * const) so that textually substituting a
typedef with its type gives the same type

Ex.
The last two lines mean same here ...

typedef int * PINT;
typedef PINT const CPINT;
typedef int * const CPINT;

Some of us, who actually happen to like const at the front, would
never ever do CPINT and PCINT typedefs anyway. They are extremely
ugly!

So there is no problem! :)
Yes, there is no point in debating this. As JK said this issue is
subjective and is more of a matter of one's taste. Better to follow
the local coding guidelines.

Right!


Bo Persson
 
J

James Kanze

(e-mail address removed) wrote:

[...]
Some of us, who actually happen to like const at the front,
would never ever do CPINT and PCINT typedefs anyway. They are
extremely ugly!

Some of us who prefer the const at the back would never use such
typedef's either:).

There are more complicated cases, where a typedef might be
appropriate. But globally, IMHO, the typedef argument is an
argument, but a weak one---the declaration syntax and semantics
of C++ are complicated enough that any C++ programmer will have
to learn them anyway, and they involve a lot more than just
reading off the elements in order.
 
J

James Kanze

What about things like volatile and mutable?

Don't use them:). Seriously, volatile is a cv-qualifier, and
everywhere I've seen it used, it has followed the rules of
const. Mutable is a bit special (in my mind, at least); I tend
to think of it almost like a storage class, and put it at the
start, but arguably, it is closer to a cv-qualifier. (The big
difference, of course, is that it can only be used in the
declaration specifier, to apply to the declared object---you
don't have e.g. mutable qualified lvalues, or pointers to
mutable.)

And of course, none of these would ever be put between int and
long, or long and unsigned. (At least, I've never seen anyone
who would do that in real code.) But something like "int
mutable long" is perfectly legal.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top