Topic: Is "typedef const T CT; const CT foo;" legal?


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 27 Dec 1993 14:51:09 GMT
Raw View
In article <2ffsd6$4ge@urmel.informatik.rwth-aachen.de> dak@hathi.informatik.rwth-aachen.de (David Kastrup) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>>From: thomson@cppfs.torolab.ibm.com (Brian Thomson)
>>>David Kastrup <dak@hathi.informatik.rwth-aachen.de> wrote:
>>>
>>>>const int is not a type. It is a type with a cv-modifier.
>>>
>>>Actually, "const int" is a type.  There is ample room for confusion
>
>Ok, I do admit that I (dak) have actually written based on what I figured
>out the language must behave like due to logic. Foolish me. Now, while
>we are at the complicated type matter, what of the following?

>extern int xxx(int);
>int xxx(const int i)
>{
> return i;
>}

>
>Is this legal? Or rather, is this the same function?

 Yes. It does as of Munich C++.

>I would have the feeling it should. The "const" here does
>not belong to the argument type (and it is not used in overloading),
>but just tells the compiler, that the function will not change the local
>copy of the variable that it gets as an argument. It therefore does
>not belong to the function interface, but to the function internals.

 Such logic is useless. The committee agreed with your
conclusion, but the argument was that this was required for
C compatibility.

>I have deleted a lot of lines (176) of very valid and good arguments
>concerning C++ language constructions, so that avid readers of this
>thread will not be overburdened with what they have read already. For
>those new in the thread, I suggest reading all referenced posts.
>
>Resume: C++ typedef and template types, from the way they are guaranteed
>to work, present more of a macro hackery trick, than of a destillation
>of an object type. C++ allows you to mix in const and volatile declarations
>(which form storage, and not type specifications of objects, in my opinion)
>at any part between typedef/template, and actual declaration, although
>once mixed in, you cannot really get rid of it again.

 Aha! In fact, IMHO the 'const' on a variable declaration
does TWO things:

 const int x = 1;

 FIRST: it marks the memory of x read-only.

 SECOND: It passes on the 'const' cv-qualifier to the
 reference type of x so that the compiler can check for
 illegal accesses to read-only memory.

The SECOND step is just a helping hand. There is no good
reason not to pass on "static" and "auto" as well.
C just happens not to.

But the one thing that the const does NOT do is make the object const.
IMHO.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Mon, 20 Dec 1993 22:25:39 GMT
Raw View
Is the following code legal C++?

    template<class T>
       struct Foo {
          void bar(const T&) {}
       };

    Foo<int> test1;             // Okay
    Foo<const int> test2;       // Error: 'const' has already been included.

This code fails under Borland C++ v3.1. I can understand the motivation for
making
    typedef const T CT; const CT foo;
illegal since if it were allowed, then the following confusing code would
be allowed:
    typedef const volatile T CVT;

    CVT a;
    const CVT b;
    volatile CVT c;
    volatile const CVT d;

    // a, b, c, d all have the same type

The problem is that when this same rule is applied to templates. Without
having an exception to this rule for template parameters, we would require
one of these solutions:
   (a) Use const_Foo<T> instead of const Foo<T>

       template<class T>
          struct const_Foo {
             void bar(T&) {}
          };

   (b) Use Foo<CONST,T> instead of const Foo<T>

       enum TypeAttribute {CONST,VAR};
       template<class T> struct TYPE { typedef T ArgumentType;};
       struct TYPE<VAR>   { typedef const T& ArgumentType; }
       struct TYPE<CONST> { typedef T& ArgumentType; }


       template<TypeAttribute attribute,class T>
          struct Foo : TYPE<attribute> {
             void bar(ArgumentType) {}
          };

       (solution (a) is a subset of this one)

   (c) Be inefficient and create more temporaries and constructor calls than
       is necessary.

       template<class T>
          struct Foo {
             void bar(T) {}
          };

Each of these solutions is (IMNSHO) an ugly and unnecessary hack.
Is the problem with Borland C++ v3.1 or is it a problem with the
evolving standard?


On a related tangent (:-]), now that the following code has been accepted
as part of the evolving standard:
    if (T* t=dynamic_cast<T>(foo))
       // ...
is the following code legal if the class SmartPtr<T> overloads "operator bool".
    if (SmartPtr<T> t=dynamic_cast<T>(foo))
       // ...
What are the exact semantics of this new construct?



Take care
    Robert
--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse               |"If we have to re-invent the wheel,       |
| EMAIL: g2devi@cdf.utoronto.ca    |  can we at least make it round this time"|
+----------------------------------+------------------------------------------/




Author: dak@hathi.informatik.rwth-aachen.de (David Kastrup)
Date: 21 Dec 1993 20:33:58 GMT
Raw View
g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:


>Is the following code legal C++?

>    template<class T>
>       struct Foo {
>          void bar(const T&) {}
>       };

>    Foo<int> test1;             // Okay
>    Foo<const int> test2;       // Error: 'const' has already been included.

const int is not a type. It is a type with a cv-modifier. These cannot be used
in typedefs, or as template types (the latter I am postulating without proof
or reference. It is how I understand it). A cv-type, however, can be used to
build a *clean type, as in
typedef const int *k;
This is legal, whereas
typedef const int l;
is not.
Also,
typedef int * const k;
is not.

About those cv-modifiers: the distinction between them, and the type is
complicated: Which of the following are const?
const int i, k; // is k const ?
int const i, k; // is k const ?
int i, const k; // is this legal ?
int const *k, l; // ...
Try this on brain and compiler.

As a result, I would strongly suggest disallowing const-ness in template
types. They do not belong to the type, but to the objects created with
that type.
>This code fails under Borland C++ v3.1. I can understand the motivation for
>making
>    typedef const T CT; const CT foo;
>illegal since if it were allowed, then the following confusing code would
>be allowed:
>    typedef const volatile T CVT;
Illegal, as explained above. The type is not supposed to include storage
properties depending on the actual objects.

>    CVT a;
>    const CVT b;
>    volatile CVT c;
>    volatile const CVT d;

>    // a, b, c, d all have the same type
--
 David Kastrup        dak@pool.informatik.rwth-aachen.de
 Tel: +49-241-72419 Fax: +49-241-79502
 Goethestr. 20, D-52064 Aachen




Author: thomson@cppfs.torolab.ibm.com (Brian Thomson)
Date: 21 Dec 1993 22:45:27 GMT
Raw View
In article <2f7mjm$r08@urmel.informatik.rwth-aachen.de>,
David Kastrup <dak@hathi.informatik.rwth-aachen.de> wrote:

>const int is not a type. It is a type with a cv-modifier.

Actually, "const int" is a type.  There is ample room for confusion
because "const" can appear in several places in the grammar:
as part of a  type-specifier  as it does in
    const int i;
or as a cv-qualifier in a declarator (after a '*', '&', '::*", or
after the parameter list of a member function).
Its scope of influence is the entire declaration in the first case, but
only the single declarator in the second.

This implies that

       typedef const int l;

is, in fact, legal.  In fact "const" and "volatile" may be added to any
type specifiers in a declaration, so

       const const int i;    //  ?

is not strictly illegal although it may be flagged as a questionable
construct.

>About those cv-modifiers: the distinction between them, and the type is
>complicated: Which of the following are const?
>const int i, k; // is k const ?
Yes.

>int const i, k; // is k const ?
Yes.

>int i, const k; // is this legal ?
No, if "const" doesn't appear in the type specifier (i.e., with the "int")
then it must follow one of '*', '&', etc.

>int const *k, l; // ...
>Try this on brain and compiler.
k is a pointer to const int.
l is a const int.

Now, these const declarations are actually invalid because they are neither
explicitly "extern" nor do they have initializers, but that is really
tangential to the issue of where "const" is permitted.


With regard to the original question about template type parameters:

    template<class T>
       struct Foo {
      void bar(const T&) {}
       };

    Foo<int> test1;             // Okay
    Foo<const int> test2;       // Error: 'const' has already been included.

I see no reason to object to this code.  Our compilers like it, too.


>>    typedef const volatile T CVT;

>>    CVT a;
>>    const CVT b;
>>    volatile CVT c;
>>    volatile const CVT d;
>
>>    // a, b, c, d all have the same type


--
Brian Thomson   THOMSON at TOROLAB
OS/2 C++ Development  thomson@vnet.ibm.com




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 21 Dec 1993 07:01:23 GMT
Raw View
In article <1993Dec20.222539.27580@cdf.toronto.edu> g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
>
>Is the following code legal C++?
>
>    template<class T>
>       struct Foo {
>          void bar(const T&) {}
>       };
>
>    Foo<int> test1;             // Okay
>    Foo<const int> test2;       // Error: 'const' has already been included.

 It legal. As per San Jose, you cant write

 const const int x=1; // error

but you can write:

 typedef const int cint;

 const cint x=1;

I hate this perversion, but it was argued its required for ISO C
compatibility.
>
>This code fails under Borland C++ v3.1. I can understand the motivation for
>making
>    typedef const T CT; const CT foo;
>illegal since if it were allowed,

 It _is_ allowed.

>then the following confusing code would
>be allowed:
>    typedef const volatile T CVT;
>
>    CVT a;
>    const CVT b;
>    volatile CVT c;
>    volatile const CVT d;
>
>    // a, b, c, d all have the same type

 They are allowed. I hate this.
>
>The problem is that when this same rule is applied to templates.

 Personally, I stated the Object Type Rule: template
arguments must not be cv-qualified or references.

 The committee didnt agree.

 But then, I prefer to think of templates as generics,
not syntax macros.

>Without
>having an exception to this rule for template parameters, we would require
>one of these solutions:
>   (a) Use const_Foo<T> instead of const Foo<T>

 You should be required to do exactly this. IMHO.

>       template<class T>
>          struct const_Foo {
>             void bar(T&) {}
>          };

[b,c illustrated]

>Each of these solutions is (IMNSHO) an ugly and unnecessary hack.
>Is the problem with Borland C++ v3.1 or is it a problem with the
>evolving standard?

 No. (a) is correct. IMHO. :-)
 I have an example which clearly indicates this.

 Being able to supply ref or cv-qualifiers for
template arguments can radically change the semantics,
so that the template does not represent a consistent family.

 And while perversions will *usually* be diagnosed,
some are not.

 If a variable in a tempate is non-const,
it should not be possible to suddenly make it const,
or a reference. The operation of the template is then
subtly changed by supplying a bogus argument.

 This is an artefact of improper understanding
of the type system, resulting from overloading
keywords and concepts, and defaulting.

 When I write "T x;" in a template I want that to
mean "x is a mutable non-volatile non-reference variable".
I do not want the user to be able to change the meaning
by supplying arguments that, by a pervesion of the
way the syntax happens to work due to defaulting
and macro style substitution, allows that "x" to become
anything else.

 If you examine classes, and ignore volatile for the
sake of argument, you will see there are three possible
specification for a data member:

 1) immutable
 2) mutable
 3) inherited

 class X {
  int x; // inherited
  const int x; // immutable always
  mutable int x; // mutable always
 };

Inherited depends on the object declaration:

 mutable X a; // a.x is mutable
 const X a; // a.x is immutable

In templates, this can be enforced by the object type rule:
there is no "inherited" behaviour. Another alternative
is to allow "mutable" on variables:

 template<class T> int f(T x) {
  mutable T y ...
 }

This promises that f<const int> is ill formed. If we can also
say

 non-ref T y

to imply y cannot be a reference, then I wil drop the
object type rule so as to allow "inheritance" of ref and cv-qual
attributes. But without a way to distinguish the intended
use of the variable I continue to believe that the Object
Type Rule should not be violated, because there is no
way for the programmer to signal er intentions so that
incorrect usage is diagnosable.

This is, IMHO, a flaw in the design of the language.

>On a related tangent (:-]), now that the following code has been accepted
>as part of the evolving standard:
>    if (T* t=dynamic_cast<T>(foo))
>       // ...
>is the following code legal if the class SmartPtr<T> overloads "operator bool".
>    if (SmartPtr<T> t=dynamic_cast<T>(foo))
>       // ...
>What are the exact semantics of this new construct?

 I guess the above is equivalent to:

 {
  T* temp t = dynamic_cast<T>(foo)
  SmartPtr<T> t = temp;
  bool b = t.operator bool();
  if(b) ...
 }


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Dec 1993 10:29:25 GMT
Raw View
In article <2f7mjm$r08@urmel.informatik.rwth-aachen.de> dak@hathi.informatik.rwth-aachen.de (David Kastrup) writes:
>g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
>
>
>>Is the following code legal C++?
>
>>    template<class T>
>>       struct Foo {
>>          void bar(const T&) {}
>>       };
>
>>    Foo<int> test1;             // Okay
>>    Foo<const int> test2;       // Error: 'const' has already been included.
>
>const int is not a type. It is a type with a cv-modifier.

 I agree your computer science is correct, BUT ...

>These cannot be used
>in typedefs, or as template types (the latter I am postulating without proof
>or reference. It is how I understand it).

 But not how C or the committee understands it.

A cv-type, however, can be used to
>build a *clean type, as in

 The word you are looking for is "object type". Except,
the C++ committee probably doesnt agree.

>typedef const int *k;
>This is legal, whereas
>typedef const int l;
>is not.

 You have made a mistake in believing that a typedef
name denotes a type. It doesnt. A typedef is a syntax macro,
that is useful because in C because of the stupid declarator
syntax, the principle of textual substitution does not work.

>Also,
>typedef int * const k;
>is not.

 How about this:

 int & const k = j; // :-)

Its hard to believe the committee decided to allow this perversion.
(I think they did.) But not

 int & volatile k = j; // :-(

The argument was that the const was harmless and that
a reference is somehow const. Grrrrr (as RFG would say :-)

>
>About those cv-modifiers: the distinction between them, and the type is
>complicated: Which of the following are const?

 Indeed, the syntax is .. [censored :-]

>const int i, k; // is k const ?
>int const i, k; // is k const ?
>int i, const k; // is this legal ?
>int const *k, l; // ...
>Try this on brain and compiler.

 C++ is designed specifcally by computer scientists
who like languages with modern syntax to turn those C hackers who
thought they were smart inside out. :-)

>As a result, I would strongly suggest disallowing const-ness in template
>types. They do not belong to the type, but to the objects created with
>that type.

 Yes. You mean as arguments to templates. Same for & (reference).

>>This code fails under Borland C++ v3.1. I can understand the motivation for
>>making
>>    typedef const T CT; const CT foo;
>>illegal since if it were allowed, then the following confusing code would
>>be allowed:
>>    typedef const volatile T CVT;
>Illegal, as explained above. The type is not supposed to include storage
>properties depending on the actual objects.

 Irrelevant. typedef is a kludge to fix lousy C syntax,
its not something that names an object type.

 You CAN write:

 const CT foo;

 You CANT write:

 const const CT foo;

 So there.

>
>>    CVT a;
>>    const CVT b;
>>    volatile CVT c;
>>    volatile const CVT d;
>
>>    // a, b, c, d all have the same type

 Aw, look at some real stupidity:

 typedef int& IR;
 const IR x; // ??
 IR& x; // ????

 Excuse my exasperation. Trying to build a superior
language with C syntax is non-trivial.

 Bjarne did a fine job, given what he had to work with.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Dec 1993 11:14:17 GMT
Raw View
>From: thomson@cppfs.torolab.ibm.com (Brian Thomson)
>David Kastrup <dak@hathi.informatik.rwth-aachen.de> wrote:
>
>>const int is not a type. It is a type with a cv-modifier.
>
>Actually, "const int" is a type.  There is ample room for confusion

 Yes. Because there are three meanings of the word type
in C++:

 (1) expression type
 (2) object type
 (3) declarative form

(1) is the type of an expression, obviously. There is
no expression type "const int". Or "int". The committee is definite
on this issue. It was decided at San Jose.

The types expressions may have are things like:

 {lvalue, int}
 {rvalue, const int}

That is, for an expression one can say its is a "lvalue of type int",
and that is the type of the expression. In particular, there are
NO reference expression types:

 int x;
 int & y= x;
 x+y;

In the expression "x+y" the types of x and y are "lvalue of type int".
In both cases.

This terminology is called "lvaluan". The alternative choice:

 int& means lvalue of type int

is called "referenese". The committee voted to adopt lvaluan
terminology, not referenese. Therefore, no expression ever
has reference type.

This is IMPORTANT. It affects overload resolution, and
template instantiation.

If an expression has the type:

 {rvalue, const int}

and is used as an argument to a function or reference initialisation,
the "const" is ignored, HOWEVER the const is NOT ignored if the
expression is used as the object of a member function call.

In other words (my interpretation) in the usual context, the
type is equivalent to

 {rvalue, int}

but as the object of a member function call the type is
equivalent to

 {lvalue, const int}

That means the type of an expression is effectively context
sensitive if you consider that binding to the object
of a member function is the same type of binding
as for ordinary references, OR, it isnt context sensitive,
but the binding rules are.

I believe the latter is the interpretation of the committee.

I also dislike either position.

>because "const" can appear in several places in the grammar:
>as part of a  type-specifier  as it does in
>    const int i;

 And this is the third meaning of "type", namely
what I call the declarative form of a declaration.

 A declaration may have "reference form", for
example:

 const int & y = x;

However while its declarative type(3) is "const reference to int",
the use of x in an expression is type(1) "const lvalue of type int" and
the type of object to which x refers is type(2) "int".

Some members of the committe believe the type of object
to which x refers is type "const int". Surely the
type of a VALUE contained in 'x' is simply "int",
and perhaps this Value Type is the fourth form of type(4). :-)


>This implies that
>
>       typedef const int l;
>
>is, in fact, legal.

 Indeed the fact that the grammar allows this and the
committee endorsed it makes it legal. That has nothing
to do with whether its sensible, and everything to do
with the fact that C allows it.

>In fact "const" and "volatile" may be added to any
>type specifiers in a declaration, so
>
>       const const int i;    //  ?
>
>is not strictly illegal although it may be flagged as a questionable
>construct.

 It is strictly illegal. Decided at San Jose.

>>int const *k, l; // ...
>>Try this on brain and compiler.
>k is a pointer to const int.
>l is a const int.

 Correct. But this has nothing to do with type(2), type(3)
or type(4) but merely type(1) which is an artefact of the grammar.

>With regard to the original question about template type parameters:
>
>    template<class T>
>       struct Foo {
>      void bar(const T&) {}
>       };
>
>    Foo<int> test1;             // Okay
>    Foo<const int> test2;       // Error: 'const' has already been included.
>
>I see no reason to object to this code.  Our compilers like it, too.

 I see every reason to. It's macro hackery. In particular,
you can completely change the meaning of a template if you get
confused and think things like "int&" is a type.

 Foo<int&> test3; // GAK

 There is no point quoting the ARM or working paper
to prove that something is or is not a type. The ARM type
system is flawed, and the terminology used ambiguous due
to overloading. Any formulation of a proper type system
is sure to break some code.

 For example

 f(const int) { }
 f(int) { } // error, duplicate definition

this is NOT what the ARM says, but it IS what both ISO C and
the current Working Paper say.  We all know that the "const"
here is useful only in the implementation of the function,
because the parameter is an lvalue, indeed a variable,
but its meaningless to talk about the constness or
otherwise of a value. Right?

Well, the change was made at Munich.  NOT for the reasons
I outline (because its correct) but because the rule
is required for ISO C compatibility.

And at San Jose,
it was decided that rvalues can be cv-qualified ...but
only when they're objects of member function calls.
And it was agreed that the C model is no longer appropriate for C++.
(In C, only lvalues may be cv-qualified)

Why all this confusion? Because grafting an imperative,
object oriented system onto an expression based value oriented
one with horrible syntax without breaking any C or much
C++ code is a little bit tricky.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: dak@hathi.informatik.rwth-aachen.de (David Kastrup)
Date: 24 Dec 1993 23:01:58 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>>From: thomson@cppfs.torolab.ibm.com (Brian Thomson)
>>David Kastrup <dak@hathi.informatik.rwth-aachen.de> wrote:
>>
>>>const int is not a type. It is a type with a cv-modifier.
>>
>>Actually, "const int" is a type.  There is ample room for confusion

Ok, I do admit that I (dak) have actually written based on what I figured
out the language must behave like due to logic. Foolish me. Now, while
we are at the complicated type matter, what of the following?
extern int xxx(int);
int xxx(const int i)
{
 return i;
}

Is this legal? Or rather, is this the same function?
I would have the feeling it should. The "const" here does
not belong to the argument type (and it is not used in overloading),
but just tells the compiler, that the function will not change the local
copy of the variable that it gets as an argument. It therefore does
not belong to the function interface, but to the function internals.

This is entirely different from
extern int xxx(int&);
int xxx(const int &i)
{
 return i;
}
where prototype and function belong to two different functions.

I have deleted a lot of lines (176) of very valid and good arguments
concerning C++ language constructions, so that avid readers of this
thread will not be overburdened with what they have read already. For
those new in the thread, I suggest reading all referenced posts.

Resume: C++ typedef and template types, from the way they are guaranteed
to work, present more of a macro hackery trick, than of a destillation
of an object type. C++ allows you to mix in const and volatile declarations
(which form storage, and not type specifications of objects, in my opinion)
at any part between typedef/template, and actual declaration, although
once mixed in, you cannot really get rid of it again.
--
 David Kastrup        dak@pool.informatik.rwth-aachen.de
 Tel: +49-241-72419 Fax: +49-241-79502
 Goethestr. 20, D-52064 Aachen