Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Acceptable to "const" a parameter in the definition but not in thedeclaration?

Reply
Thread Tools

Acceptable to "const" a parameter in the definition but not in thedeclaration?

 
 
jl_post@hotmail.com
Guest
Posts: n/a
 
      06-08-2010
Hi,

I'm a big fan of the "const" keyword for several reasons. I don't
want to get too deep in my reasons, but one place I'll use them is in
code like this:

const int numToUse = getSomeNumber(...);

Since I declared numToUse as const, it's clear to the maintainer that
numToUse will not change for the rest of its scope, nor is it supposed
to change (preventing a maintainer from accidentally changing it).

At any rate, sometimes I create a function/method that has a
signature like the following:

void f(int arg1);

But for my example, arg1 is essentially a constant, since it never
changes. So I COULD declare f() as:

void f(const int arg1);

but that const is basically unneeded for the maintainer who only needs
to know the signature of f(). However, putting the const keyword in
tells whoever looks at the definition of f() that arg1 is never
supposed to change.

(Note that I am not passing by reference -- only by value.)

So the advantage of putting const in is that the maintainer who
reads the definition of f() can tell that arg1 is never supposed to
change in f(). But this information is unimportant to the maintainer
who treats f() as a "black-box" -- to him/her it makes no difference
if arg1 (which is passed by value) is changed in f(), so seeing arg1
declared as const is of no help at all.

Playing around with this, I discovered that I can declare arg1 as
const in the definition, like this:

void f(const int arg1) { ... }

while not declaring arg1 as const in the declaration/signature/
prototype, like this:

void f(int arg1);

The compiler will compile this just fine, despite that "const" is not
in both lines. (I tried this on Visual C++, g++ on Win32, and g++ on
Linux.)

If you'd like to see a full working program that illustrates what
I'm talking about, here is one:


#include <iostream>

void f(int arg1);

int main(int argc, char ** argv)
{
f(77);

return 0;
}

void f(const int arg1)
{
std::cout << "arg1 = " << arg1 << std::endl;
}


So my question is: Is this an acceptable thing to do (as far as the
C++ language specifications are concerned)?

(Just because it works on all compilers I've tried it on so far
doesn't necessarily mean it's allowed in the standard language, so I'd
like to know for sure if it's a legal thing to do.)

Being able to do this is good in that the maintainers of the
function can see what passed-in arguments are never supposed to
change, while the other maintainers can treat the function as a black-
box and not need to see which passed-in-by-value arguments are const.

Thanks.

-- Jean-Luc
 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      06-08-2010
On 6/8/2010 11:48 AM, http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> I'm a big fan of the "const" keyword for several reasons. I don't
> want to get too deep in my reasons, but one place I'll use them is in
> code like this:
>
> const int numToUse = getSomeNumber(...);
>
> Since I declared numToUse as const, it's clear to the maintainer that
> numToUse will not change for the rest of its scope, nor is it supposed
> to change (preventing a maintainer from accidentally changing it).
>
> At any rate, sometimes I create a function/method that has a
> signature like the following:
>
> void f(int arg1);
>
> But for my example, arg1 is essentially a constant, since it never
> changes. So I COULD declare f() as:
>
> void f(const int arg1);
>
> but that const is basically unneeded for the maintainer who only needs
> to know the signature of f(). However, putting the const keyword in
> tells whoever looks at the definition of f() that arg1 is never
> supposed to change.
>
> (Note that I am not passing by reference -- only by value.)
>
> So the advantage of putting const in is that the maintainer who
> reads the definition of f() can tell that arg1 is never supposed to
> change in f(). But this information is unimportant to the maintainer
> who treats f() as a "black-box" -- to him/her it makes no difference
> if arg1 (which is passed by value) is changed in f(), so seeing arg1
> declared as const is of no help at all.
>
> Playing around with this, I discovered that I can declare arg1 as
> const in the definition, like this:
>
> void f(const int arg1) { ... }
>
> while not declaring arg1 as const in the declaration/signature/
> prototype, like this:
>
> void f(int arg1);
>
> The compiler will compile this just fine, despite that "const" is not
> in both lines. (I tried this on Visual C++, g++ on Win32, and g++ on
> Linux.)
>
> If you'd like to see a full working program that illustrates what
> I'm talking about, here is one:
>
>
> #include<iostream>
>
> void f(int arg1);
>
> int main(int argc, char ** argv)
> {
> f(77);
>
> return 0;
> }
>
> void f(const int arg1)
> {
> std::cout<< "arg1 = "<< arg1<< std::endl;
> }
>
>
> So my question is: Is this an acceptable thing to do (as far as the
> C++ language specifications are concerned)?


What is? The top-level const qualifiers for arguments or return value
are ignored and do not contribute to the function type, so

void f(int);

and

void f(int const);

are two declarations of the same function.

As to acceptability, do what your coding standard tells you. In our
group if the function is declared in some way, you should be able to
look for the declaration using textual search, which means we keep even
the names of the arguments the same...

> (Just because it works on all compilers I've tried it on so far
> doesn't necessarily mean it's allowed in the standard language, so I'd
> like to know for sure if it's a legal thing to do.)


Yes.

> Being able to do this is good in that the maintainers of the
> function can see what passed-in arguments are never supposed to
> change, while the other maintainers can treat the function as a black-
> box and not need to see which passed-in-by-value arguments are const.


In our neck of the woods we say, whatever floats your boat.

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      06-08-2010
* (E-Mail Removed), on 08.06.2010 17:48:
>
> #include<iostream>
>
> void f(int arg1);
>
> int main(int argc, char ** argv)
> {
> f(77);
>
> return 0;
> }
>
> void f(const int arg1)
> {
> std::cout<< "arg1 = "<< arg1<< std::endl;
> }
>
>
> So my question is: Is this an acceptable thing to do (as far as the
> C++ language specifications are concerned)?


It's standard-conforming, yes.

However, only top level 'const' is disregarded for forming the function's type.

E.g. 'int const' -> 'int', but not 'int const*' -> 'int*'.


Cheers & hth.,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Jorgen Grahn
Guest
Posts: n/a
 
      06-08-2010
On Tue, 2010-06-08, Alf P. Steinbach wrote:
....
> However, only top level 'const' is disregarded for forming
> the function's type.
>
> E.g. 'int const' -> 'int', but not 'int const*' -> 'int*'.


Which is a slightly convoluted way of saying "only the things that are
surely irrelevant to the interface are disregarded in the interface".

When you use call-by-value, you couldn't care less if the callee
modifies his copy of the value. Easy to remember, and makes good
sense.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      06-08-2010
* Jorgen Grahn, on 08.06.2010 22:09:
> On Tue, 2010-06-08, Alf P. Steinbach wrote:
> ...
>> However, only top level 'const' is disregarded for forming
>> the function's type.
>>
>> E.g. 'int const' -> 'int', but not 'int const*' -> 'int*'.

>
> Which is a slightly convoluted way of saying "only the things that are
> surely irrelevant to the interface are disregarded in the interface".


That turns out to be a circular argument, because (see below) the 'const' is
only irrelevant because of the current rules that it is disregarded...


> When you use call-by-value, you couldn't care less if the callee
> modifies his copy of the value. Easy to remember, and makes good
> sense.


Yes, easy to remember, but a bit misleading.

Consider the translation of a function

void foo( T v );

to machine code, and in particular, for sizeof(T) > something, implementing the
function in machine code as if it were declared as

void foo( T* v );

which in certain cases may reduce the argument passing from a large number of
bytes to four or eight bytes.

If there is possible aliasing, that some code invoked by foo (or perhaps
executing in a separate thread) accesses the caller's actual argument, then this
optimization is unsafe unless a copy of the actual argument is made. The copy
can made at the call site or internally in foo. The latter was not uncommon in
the old days, at least for Pascal compilers, because when foo does nothing with
the argument but passing it on, then the copying can be avoided.

Placing the copying at the call site, on the other hand, allows that copying to
be elided when the compiler can prove that there's no aliasing and can assume
that foo does not change the actual argument via the passed pointer. So this
could be an optimization. But if the source code implementation of foo is e.g.

void foo( T v ) { v = bar(); blahblah(); use( v ); }

then, at least for local code generation, the code generated for foo would also
have to copy v, resulting in potentially two copy operations per call (one at
each call site and one inside foo) instead of just one: hardly an optimization!

However, if a 'T const' in the declaration guaranteed a 'T const' in the
definition, then the compiler could do this optimization, perhaps with some
wording in the standard that you're in UB-land if you cast away the const.
Because then the compiler could assume that foo would not modify the actual
argument via the pointer. All it'd have to do would be to prove no aliasing for
any particular call site, and then it could just pass a pointer with no copying.

Essentially, any restriction enforced by the type system increases what is known
and so may facilitate some optimization, not just correctness.

And so also here.


Cheers,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      06-08-2010
* Paavo Helde, on 09.06.2010 00:33:
> "Alf P. Steinbach"<(E-Mail Removed)> wrote in
> news:humcfr$7l7$(E-Mail Removed)-september.org:
>
> [...]
>
>> Placing the copying at the call site, on the other hand, allows that
>> copying to be elided when the compiler can prove that there's no
>> aliasing and can assume that foo does not change the actual argument
>> via the passed pointer. So this could be an optimization. But if the
>> source code implementation of foo is e.g.
>>
>> void foo( T v ) { v = bar(); blahblah(); use( v ); }
>>
>> then, at least for local code generation, the code generated for foo
>> would also have to copy v, resulting in potentially two copy
>> operations per call (one at each call site and one inside foo) instead
>> of just one: hardly an optimization!
>>
>> However, if a 'T const' in the declaration guaranteed a 'T const' in
>> the definition, then the compiler could do this optimization, perhaps
>> with some wording in the standard that you're in UB-land if you cast
>> away the const. Because then the compiler could assume that foo would
>> not modify the actual argument via the pointer. All it'd have to do
>> would be to prove no aliasing for any particular call site, and then
>> it could just pass a pointer with no copying.

>
> If the compiler can prove the function does not modify the argument, it
> can apply this optimization anyway, regardless of whether there is
> 'const' present or not.


That "if" is pretty big: with the rules that we have it would require whole
program optimization and is not possible with dynamic libraries without
introducing overhead (the opposite of optimization).

Since the context has been lost, and you're arguing below is if the context was
very different than what it actually was:

The context for the above quote was whether a possible rationale for the current
rules held water or not, and it did not hold water.


> And about adding new UB in the language - I would have thought there is
> already too much of that. Besides, such a change could break existing
> code in very annoying ways.


I discussed the rationale for the existing rules. I did not discuss changing the
rules. In particular, I did not advocate introducing new UB.


>> Essentially, any restriction enforced by the type system increases
>> what is known and so may facilitate some optimization, not just
>> correctness.

>
> Except the 'const' keyword in interfaces, which can be cast away legally
> in most cases.


It's unclear what you're referring to.


> Banning const_cast would indeed resolve this problem,


It's unclear what you're referring to.


> but
> would create many more instead.


I did not propose or discuss "banning const_cast". With C++98 rules const_cast
for an original const object is UB if you modify the result. The alternative
rules I discussed would presumably have extended that to formal arguments.


> I think I will stick to passing const references, it's just one more
> character to type after all.


That statement does not make sense to me, I'm sorry.


Cheers,

- Alf


--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      06-09-2010
* Fred Zwarts, on 09.06.2010 09:24:
> <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed)
>
>> #include<iostream>
>>
>> void f(int arg1);
>>
>> int main(int argc, char ** argv)
>> {
>> f(77);
>>
>> return 0;
>> }
>>
>> void f(const int arg1)
>> {
>> std::cout<< "arg1 = "<< arg1<< std::endl;
>> }
>>
>>
>> So my question is: Is this an acceptable thing to do (as far as the
>> C++ language specifications are concerned)?

>
> Maybe,


It is standard-conforming.


> but I have to work with a compiler which does not accept it.


Which compiler is that?


Cheers,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Fred Zwarts
Guest
Posts: n/a
 
      06-10-2010
"Alf P. Steinbach" <(E-Mail Removed)> wrote in message
news:huo725$7c2$(E-Mail Removed)-september.org
> * Fred Zwarts, on 09.06.2010 09:24:
>> <(E-Mail Removed)> wrote in message
>> news:(E-Mail Removed)
>>
>>> #include<iostream>
>>>
>>> void f(int arg1);
>>>
>>> int main(int argc, char ** argv)
>>> {
>>> f(77);
>>>
>>> return 0;
>>> }
>>>
>>> void f(const int arg1)
>>> {
>>> std::cout<< "arg1 = "<< arg1<< std::endl;
>>> }
>>>
>>>
>>> So my question is: Is this an acceptable thing to do (as far as
>>> the C++ language specifications are concerned)?

>>
>> Maybe,

>
> It is standard-conforming.
>
>
>> but I have to work with a compiler which does not accept it.

>
> Which compiler is that?


The OpenVMS C++ 6.5 compiler for Alpha processors.
I known it is an old version, but it is something I have to live with.

The code:

int func (int);

int func (const int i) {
return i;
}

results in:

%CXX-W-NOTQUACOMPREDEC, declaration is not qualifier compatible with
"int func(int)" (declared at line 1)
at line number 3 in file SCRATCH_ROOT:<KLAD>T.CPP;1

This warning is issued even when the compiler runs in ANSI-standard mode.
It should be noted that this warning can be suppressed with a compiler option.
 
Reply With Quote
 
Tiib
Guest
Posts: n/a
 
      06-10-2010
On Jun 10, 10:55 am, "Fred Zwarts" <(E-Mail Removed)> wrote:
>
> The OpenVMS C++ 6.5 compiler for Alpha processors.
> I known it is an old version, but it is something I have to live with.
>
> The code:
>
> int func (int);
>
> int func (const int i) {
> return i;
>
> }
>
> results in:
>
> %CXX-W-NOTQUACOMPREDEC, declaration is not qualifier compatible with
> "int func(int)" (declared at line 1)
> at line number 3 in file SCRATCH_ROOT:<KLAD>T.CPP;1
>
> This warning is issued even when the compiler runs in ANSI-standard mode.
> It should be noted that this warning can be suppressed with a compiler option.


It should be also noted that compiler may issue diagnostics about
everything that it finds rude, funny, outstanding or otherwise
remarkable during compiling. If it does not entirely refuse to compile
a piece of code that is valid by standard it is all OK. Alf has whole
entry in his blog how to turn off sillywarnings (his term) of
microsoft compilers.
 
Reply With Quote
 
Jorgen Grahn
Guest
Posts: n/a
 
      06-12-2010
On Tue, 2010-06-08, Alf P. Steinbach wrote:
> * Jorgen Grahn, on 08.06.2010 22:09:
>> On Tue, 2010-06-08, Alf P. Steinbach wrote:
>> ...
>>> However, only top level 'const' is disregarded for forming
>>> the function's type.
>>>
>>> E.g. 'int const' -> 'int', but not 'int const*' -> 'int*'.

>>
>> Which is a slightly convoluted way of saying "only the things that are
>> surely irrelevant to the interface are disregarded in the interface".

>
> That turns out to be a circular argument, because (see below) the 'const' is
> only irrelevant because of the current rules that it is disregarded...


I was a bit too lazy to read your longer text carefully, but I suppose
I'm relying on the traditional meaning of call-by-value here, the one
you're taught in CS classes. *That* is what makes the const irrelevant
to the caller. I don't see C++ moving away from call-by-value any time
soon.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
In order to cross platform, Ruby is designed to be interpreted inruntime, so Ruby code is exposed on the server. This brings a security dangerwhich is not acceptable. Erwin Moller Ruby 9 05-08-2008 01:17 PM
Not quite standard... but socially acceptable Toms hilidhe C Programming 20 04-04-2008 10:46 PM
It's strange that the address of array elements are not acceptable template-arguments!! Wayne Shu C++ 8 01-29-2007 08:28 PM
type 'slice' is not an acceptable base type Antoon Pardon Python 2 12-21-2005 09:36 AM
huge numbers are not acceptable , why? ArShAm C++ 6 11-10-2003 05:28 PM



Advertisments