From -8882820719236681608
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f78e5,ede379f5dbbc1549
X-Google-Attributes: gidf78e5,public
From: niklasb@my-deja.com
Subject: Re: Suggestion: "typeof" keyword
Date: 1999/08/19
Message-ID: <7phk8n$b26$1@nnrp1.deja.com>
X-Deja-AN: 514829337
X-NNTP-Posting-Host: 131.107.3.77
Approved: stephen.clamage@sun.com (comp.std.c++)
References: <7nt3vl$1oe$1@nnrp1.deja.com> <7oh7o8$t3f$1@nnrp1.deja.com> <7ol6hi$ej9$2@news.BelWue.DE> <cTiioXAhAwr3Ew$X@robinton.demon.co.uk> <t7d7ww1vrz.fsf@calumny.jyacc.com> <e4DSyfA4ALs3Ew0Q@robinton.demon.co.uk> <t7r9laz5hj.fsf@calumny.jyacc.com> <7oup0a$8g2$1@nnrp1.deja.com> <37B3C2AD.5AB722AE@lucent.com> <37B5A571.449B@wanadoo.fr> <37B908A8.717CAC2C@lucent.com> <CVJFZFAbaWu3Ew15@robinton.demon.co.uk> <7pcvjs$tk4$1@nnrp1.deja.com> <user-1808991258230001@aus-as3-121.io.com>
X-UID: 0000000001
X-Status: $$$T
X-Http-User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)
X-Http-Proxy: 1.0 RED-PRXY-07, 1.0 x42.deja.com:80 (Squid/1.1.22) for client 131.107.3.77
Organization: Deja.com - Share what you know. Learn what you don't.
X-Article-Creation-Date: Thu Aug 19 19:00:48 1999 GMT
X-MyDeja-Info: XMYDJUIDniklasb
Newsgroups: comp.std.c++
Originator: clamage@taumet


In article <user-1808991258230001@aus-as3-121.io.com>,
  postmast.root.admi.gov@iname.com (blargg) wrote:
> In article <7pcvjs$tk4$1@nnrp1.deja.com>, niklasb@my-deja.com wrote:
>
> [snip]
>
> >
> > 1 The typeof operator yields the type of its operand.
> >   The operand is either an expression, which is not
> >   evaluated, or a parenthesized type-id.
>
> The latter refers to typeof (int) for example. What is that for?

You're absolutely right. I thought of this too after I posted.
I would revise the first rule as follows:

1 The typeof operator yields the type of its operand.
  The operand is an expression, which is not evaluated.
  The result is the type of the expression (the static
  type), not the dynamic type of the object returned
  by the expression.

>
> > 2 When applied to an expression, the result is the type
> >   of the expression (the static type), not the dynamic
> >   type of the object returned by the expression.
>
> I think some people were confused on this :-)  This is one of
those "duh" items.

Yes, but some people on this newsgroup have been confused
about this (even in the case of sizeof), so I thought
clarification necessary. Since it is just a clarification,
perhaps it should just be lumped under #1.

> > 3 When applied to a reference or a reference type,
> >   the result is the referenced type. When applied
> >   to a constant reference or constant reference type,
> >   the resulting type is not constant.
>
> Bad. Why not allow the user to decide to remove the reference?

This is certainly an arguable point. Before responding to
your arguments, let me explain why I included this paragraph
in the first place.

My intuition says that if 'p' is an int* or an int[] then
typeof(*p) should be int. You could argue for int&, but I
suspect most people think of *p is denoting the integer
itself, not a refence to it.

Now suppose 'p' is, say, a vector<int>::iterator. Once
again, I would expect typeof(*p) to be int -- even though
the return type for the iterator's operator* is int&. This
is because the intuitive meaning of operator* is to take
an expression that denotes a pointer object and yield an
expression that denotes the thing pointed to. The '&' in
the return type is there simply to allow *p to appear on
the left-hand side of an assignment operator.

Every special case should be treated with suspicion, but
references are special in many ways. For example, sizeof
applied to a reference yields the size of the referenced
object, not the reference itself. This follows from the
idea that a reference is best thought of as a kind of
alias for another object rather than an object in its
own right. The proposed rule for typeof would parallel
the rule for sizeof on this respect.

I'm prepared to accept that my intuition may differ from
the norm. It would be interesting to try both designs
with a large number of programmers and find out which
results in more surprises.

> Bad. Call-through doesn't work.
>
>     int& func();
>
>     void g() {
>         typeof (func()) ret = func();
>         ret = 20; // oops, modified local variable ret
>     }

This could be an oops, or it could be what you expect.

If typeof did yield an int&, I could very well be surprised
that changing ret also changed some other value. There is
nothing in function g() to suggest that ret is a reference
*unless* I look up the declaration of func().

> If they don't want the reference, they can remove it themselves:
>
>     template<typename T>
>     struct remove_ref {
>         typedef T type;
>     };
>
>     template<typename T>
>     struct remove_ref<T&> {
>         typedef T type;
>     };
>
>     void g() {
>         remove_ref<typeof (func())>::type ret = func(); // type is int
>
>         ret = 20; // OK, we know we made a copy
>     }

That is clever. But given the rule that typeof never yields
a reference type, it's much easier to add the reference
qualifier where needed:

void g() {
    typeof(func())& ret = func();
	ret = 20; // obviously modifying a reference
}

This makes it obvious that ret is a reference. We can also
tell -- looking only at g() -- that func() must return a
reference; otherwise assigning its return value to ret would
be illegal.

>
> >     vector<int> v;
> >     typeof(v[0]) a; // int, not int&
> >
> >     const vector<int>& cv = v;
> >     typeof(cv[0]) b; // int, not const int&
>
> And your example shows exactly why it shouldn't be that way.

Our intuitions appear to differ. If p is an int*, what do you
think typeof(*p) should be? (In any case, it should be spelled
out, which I didn't do.)

My feeling is that the result of the statement

  typeof(y) x = y;

should always be that x is a copy of y, never that x is a
reference to y. If you wanted the latter you could say:

  typeof(y)& x = y;

> Like your argument (I think it was you) for not removing const, I
argue
> for not removing ref, since both can be done by the user with a
template.

You must be thinking of someone else. My feeling is that
the "right-most" const/volatile qualifier should be removed:

  typeof((const int)0)         // int
  typeof((int const* const)0)  // int const*

My reasoning is that a copy of a const or volatile object
need not be const or volatile. However, a copy of a pointer
to a const or volatile object must be a pointer to a const
or volatile object. Example:

  char const* p = "foo";
  typeof(*p) ch = *p;  // ch is a copy of *p

My expectation here would be that ch is just a copy of the
character p points to, and has type char (not const char
or const char&). You could easily add const if needed.

> Not that I think this construct is very good. Having to duplicate the
> expression in many cases is just lame. I like "declare" ("var") much
> better.

Declare would be nice, but would not eliminate the need
for typeof. Rather than add two new keywords, I like the
idea, suggested by someone else, of typeof(...), meaning
"the type of the initializer or rhs".

You would need typeof, rather than declare, to specify
the return type or a parameter type of a template
function. For example:

  template<class II>
  II find(II begin, II end, const typeof(*II())& val)

Because the third parameter's type is a function of
the iterator type, the compiler could do better
parameter validation when you call this template
function. The compiler knows something about the
expected type of the first parameter (it must define
operator*), and given the first two parameters it
knows the exact type of the third.

The following example shows how typeof(...) would be
used, and how typeof can allow for better validation
of template function parameters:

  std::string fred("fred");
  unsigned char ch = 'd';
  typeof(...) imatch = find(fred.begin(), fred.end(), ch);
    // Error: cannot convert parameter 3 from unsigned char to char

> > 4 The typeof operator can be applied to a function,
> >   a function pointer or a function pointer type.
> >
> >     int F();
> >     typedef int (*FPtr)();
> >
> >     FPtr p0 = F;
> >     typeof(FPtr) p1 = F; // function ptr. type
>
> Again, what is the purpose of allowing a type in typeof? It seems
redundant.
>
>     typeof (typeof (typeof (typeof (typeof (typeof (int)))))) i =
1234;

Yes, I agree.

> >     typeof(p1) p2 = F;   // function ptr.
> >     typeof(&F) p3 = F;   // function ptr.
> >     typeof(F)* p4 = F;   // function
> >
> > 5 In the context of a typeof expression, if T is any
> >   type then T() denotes a temporary object of that
> >   type, even if the type has no default constructor.
> >   (The expression is not evaluated, so it does not
> >   matter whether the object can be correctly
> >   initialized.)
>
> Joy. I don't like typeof in practice :-)
>
> [snip]

The reason for this rule is to allow typeof to be used
where you don't have actual variables to work with, as
in the declaration of 'find' above. This is also the
reason a 'declare' feature would not be an adequate
substitute for typeof.

I think typeof would be valuable. Traits classes are
the existing alternative, but they make user code
syntactically more complex and place a greater burden
on library designers (because the library must define
the type relationships using typedefs, etc.) A typeof
operator would be easier to use, and would take
advantage of information the compiler already has.

--Nick


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]




