Clarifying 'const' terminology

Dave Dave_member at pathlink.com
Sat Jul 8 17:38:01 PDT 2006


Sean Kelly wrote:
> Bruno Medeiros wrote:
>> Derek Parnell wrote:
>>> I'm not from a C++/C#/Java background, so please excuse my lack of 
>>> understanding about the meaning that is being applied to the term 
>>> 'const' in these recent discussions.
>>>
>>
>> I think many times people talked a bit vaguely with their meaning of 
>> const.
>>
>>> To me, there seems to be two things being talked about.
>>>
>>> (a) Data, which once initialized, is not to be modified for the 
>>> run-time life of the application.
>>>
>>> (b) Data, which once passed to some function, is not to be modified 
>>> for the run-time life of that function.
>>>
>>> Walter's proposed refinement of the 'in' keyword does nothing for 
>>> type (a), and only limited support for type (b), in that it protects 
>>> data from the called function but not from functions that that 
>>> subsequently calls. It is only protection for one level deep. And the 
>>> only data protected is class objects and not other reference types.
>>>
>>> Is there any other types (or subtypes) of 'const' meanings being 
>>> discussed or considered?
>>>
>>> For example, are we distinguishing between compile-time protection 
>>> and run-time protection?
>>>
>>
>> 'const', in a general sense, is a mechanism to specify whether a 
>> variable/data/object can or can not modified, and enforce that 
>> contract (at compile time). 'const' is usually in the sense of (b), 
>> that is, some parts of code may modify the data, while others can only 
>> read it (thus it is used for ownership management). That's the case 
>> for C++.
>> Enforcing the contract means you cannot assign a const variable to a 
>> non-const one, or similarly, pass a const variable to a function that 
>> expects a non-const argument. The only way to subvert this is with a 
>> cast (which should be the only way to subvert anything).
> 
> In the realm of compiler optimization techniques, there's another issue 
> as well: can the compiler assume that, because it is evaluating a const 
> reference, that the underlying data will not change while that reference 
> is in use.  This is the truly sticky part, and is I think a large reason 
> why Walter hasn't settled on anything yet.  For example, consider "const 
> by default":
> 
>     void readWrite( inout T val ) {}
>     void readOnly( in T val ) {}
> 
>     T val;
> 
>     readWrite( inout val );
>     ...
>     readOnly( val );
> 
> In the above, because a mutable reference to val is obtained before 
> readOnly is called, can the compiler assume that the contents of val 
> will not change while readOnly is being evaluated?  Can it ever make 
> that assumption for the entire life of the program after readWrite is 
> called?
> 

For either const by default or most of Walter's 'in' proposal (for byref 
function parameters), there may be a solution at the bottom of:

http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/39757

It does rely on a non-release-build runtime check (just like array 
bounds checking) so it isn't perfect, but at least it's consistent with 
other language and reference implementation features, and as such I 
think the optimization techniques you mention could be safely applied 
for 'const' parameters. That would truly be cool IMHO.

I think this would make const-by-default or 'in' parameters a lot easier 
to debug and safer for passing byref args. around in multi-threaded 
apps. too.

Even for non-release builds the assert(x !is y) would be substantially 
faster than bounds checking in simple loops so the overhead isn't that 
terrible.

>> There are some variations in const semantics. In C++, const is a 
>> type-modifier, meaning that const can be applied to each type element, 
>> and not the "variable as whole". Best to give an example. In C++ you 
>> can have:
>>   const int * iptr = ...;
>>   int const * iptr = ...;
> 
> These two are actually identical.  Though things can get a bit tricky if 
> 'int' is replaced by a typedef involving a reference type, which is why 
> the latter form tends to be preferred these days--it is unambiguous, 
> unlike the former form.
> 
>>   const int const * iptr = ...;
>> each with different meanings:
> 
> I believe this is the same as the above, you merely applied the const to 
> int twice.  What you probably meant for these three was:
> 
>     const int * iptr = ...; // mutable ptr to const int
>     int const * iptr = ...; // same as previous line
>     int * const iptr = ...; // const ptr to mutable int
>     const int * const iptr = ...; // const ptr to const int
>     int const * const iptr = ...; // same as previous line
> 
> To return to the typedef issue for a moment, if you do:
> 
>     typedef char* pchar;
> 
>     template<class T> void func( const T val ) {}
> 
>     pchar val = "hello";
> 
>     func( val );
> 
> then it's not entirely clear whether the val in func is a const pointer 
> to mutable data or vice-versa.  Thus the preferred method is:
> 
>     template<class T> void func( T const val ) {}
> 
> Where it's obvious that the 'const' will apply to the pointer if one 
> exists.
> 
> 
> Sean



More information about the Digitalmars-d-learn mailing list