Topic: Typeof considered harmful (long)
Author: "David Abrahams" <david.abrahams@rcn.com>
Date: Sat, 17 Nov 2001 00:26:10 GMT Raw View
"Brian Parker" <brianjparker@ozemail.com.au> wrote in message
news:BZ6I7.1$2Z5.173@ozemail.com.au...
>
> "Peter Dimov" <pdimov@mmltd.net> wrote in message
> > Correct. typeof() should not target this problem. It's better solved
> > by using a helper function template or by introducing a separate "let
> > x = expr" syntax.
>
> Yes, if typeof() was introduced I think this would be very useful.
My preferred syntax (an idea stolen from Andy Koenig - I don't know who he
might have stolen it from ;-)) is:
template <typename T> T x = some * expression;
Which also allows:
template <typename T> T const& x = some * expression;
And many other variants.
Andy also suggested being able to use these things as function arguments:
void f(template <typename T> T* p);
Which just gives an alternate syntax for function templates. I'm not sure
how useful this one is...
-Dave
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: Michiel Salters<Michiel.Salters@cmg.nl>
Date: Mon, 19 Nov 2001 17:08:22 GMT Raw View
In article <v27I7.4$2Z5.854@ozemail.com.au>, Brian Parker says...
>
>
>"Michiel Salters" <Michiel.Salters@cmg.nl> wrote in message
>news:58LH7.21413$xS6.32670@www.newsranger.com...
[SNIP]
>> In addition, it is a major disadvantage that this solution does not work
>> in class templates. Many of the uses of typeof are not associated with
>> an initializing expression in such context, but are used to define
>> class members.
>
>Hmmm... this could be a major limitation of this proposal (or at least might
>suggest that typeof( ) would still be useful in addition to this proposal).
>Do you have a specific example where a class member type would need type
>typeof()?
Well, an example could be
template < template < typename T > Container >
class range {
typedef typeof( Container().begin()) iterator;
iterator begin;
iterator end;
public:
range( Container const& c ) :
begin( c.begin() ),
end ( c.end() )
{ /***/ }
};
which works even if Container was sloppy enough not to define
Container::iterator. In general, if class template C is parameterized
on type T, and it wants to store the result of T::f() in a member, it
needs to know the type of T::f() when C is defined. The actual
location of the call to T::f() may be in a different file. It might not
even be in the implementation of C's member functions.
PS. I think the error messages could be quite clear for such an example,
if e.g. c.end() had a different return type as c.begin() :
"Cannot convert c.end() to typeof( c.begin() )" .
Regards,
--
Michiel Salters
Consultant Technical Software Engineering
CMG Trade, Transport & Industry
Michiel.Salters@cmg.nl
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Brian Parker" <brianjparker@ozemail.com.au>
Date: Wed, 14 Nov 2001 10:18:41 GMT Raw View
"Peter Dimov" <pdimov@mmltd.net> wrote in message
news:7dc3b1ea.0111120843.5dd74b71@posting.google.com...
> "Brian Parker" <brianjparker@ozemail.com.au> wrote in message
news:<fTlH7.2855$c87.79835@ozemail.com.au>...
>
> This makes the function signature depend on the definition. If you
> replace the definition with a declaration:
>
> template<typename R, typename P1, typename P2, typename L>
> R func(P1 p1, P2 p2);
>
> R and L can no longer be deduced.
But for template functions defined in the header, the definition will be
available to deduce the return type by the end of the compilation unit.
Though as has been discussed in another response, an exported template
function may be problem.
>...
> > Although this solves the main problem, there are several problems with
> > typeof-
> >
> > (1) For local variables it is unnecessarily verbose- it redundantly
repeats
> > the initializing expression, leading to the possibility of obscure
errors
> > (which would be difficult to track down as the final compile-time types
> > would not be visible).
>
> Correct. typeof() should not target this problem. It's better solved
> by using a helper function template or by introducing a separate "let
> x = expr" syntax.
Yes, if typeof() was introduced I think this would be very useful.
,Brian Parker
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Wed, 14 Nov 2001 10:32:26 GMT Raw View
"Brian Parker" <brianjparker@ozemail.com.au> wrote in message
news:fTlH7.2855$c87.79835@ozemail.com.au...
> Hi all,
>
> The following is a proposal I have been considering that gives the
benefits
> of typeof with only minor extensions to the current C++ type deduction
> rules...
[introduction deleted]
> The typeof solution:
> It has been proposed to add a typeof() operator to the language. This
> operator would, similarly to sizeof, not evaluate its contained expression
> but would deduce the expression's type at compile time. With typeof(), the
> above function can have all of its template parameters deduced by adding
> typeofs in the appropriate places-
>
> template<typename P1, typename P2 >
> typeof((P1( ) * P2( ) )* P2( )) func(P1 p1, P2 p2)
> {
> typeof(p1 * p2) local = p1 * p2;
>
> return local * p2;
> }
>
> Although this solves the main problem, there are several problems with
> typeof-
>
> (1) For local variables it is unnecessarily verbose- it redundantly
repeats
> the initializing expression, leading to the possibility of obscure errors
> (which would be difficult to track down as the final compile-time types
> would not be visible).
For local variables, I think that there are better ways to automatically get
the type of the local variable. In today's language we can usually use a
traits class, although I'd certainly agree that this is often not ideal. In
a thread on this newgroup on Mar 24, 2001 ("Proposal: two real uses for the
"auto" keyword"), David Abrahams proposed a solution to this which, in my
opinion, is rather tidier. With his suggestion, the local variable could
just be initialised as
auto local = p1 * p2;
I think that your suggestion of using addition template parameters for the
types of locals will lead to a very rapid escalation in the number of
template parameters per function.
--
Richard Smith
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Brian Parker" <brianjparker@ozemail.com.au>
Date: Sun, 11 Nov 2001 17:06:18 GMT Raw View
Hi all,
The following is a proposal I have been considering that gives the benefits
of typeof with only minor extensions to the current C++ type deduction
rules...
Introduction:
The type deduction mechanism of C++98 has several limitations that
unnecessarily limit aspects of generic programming. As a solution for these
limitations, it has often been proposed that a typeof() operator be added as
a language extension. In this paper, it is shown that typeof(), whilst
solving most such problems, is syntactically clumsy and has its own
limitations. As an alternative, the semantics of type deduction in C++ can
be extended without adding a new keyword to produce a more powerful, more
elegant, and easier to use solution. If this extension was implemented, the
language would have much more complete support for generic programming.
Problem Statement:
The fundamental problem is that in a template function such as the
following:
template<typename R, typename P1, typename P2, typename L>
R func(P1 p1, P2 p2)
{
L local = p1 * p2;
return local * p2;
}
When called in the typical fashion (without explicit template specification)
MyType1 p1;
MyType2 p2;
MyType3 p3;
p3 = func(p1, p2);
ISO C++ will do type deduction to deduce the type of arguments P1 and P2,
but will not perform type inference of type L or type R. Such unknown local
variable and return types occur in generic functions which can be
instantiated with any P1 and P2.
The typeof solution:
It has been proposed to add a typeof() operator to the language. This
operator would, similarly to sizeof, not evaluate its contained expression
but would deduce the expression's type at compile time. With typeof(), the
above function can have all of its template parameters deduced by adding
typeofs in the appropriate places-
template<typename P1, typename P2 >
typeof((P1( ) * P2( ) )* P2( )) func(P1 p1, P2 p2)
{
typeof(p1 * p2) local = p1 * p2;
return local * p2;
}
Although this solves the main problem, there are several problems with
typeof-
(1) For local variables it is unnecessarily verbose- it redundantly repeats
the initializing expression, leading to the possibility of obscure errors
(which would be difficult to track down as the final compile-time types
would not be visible).
(2) For the return type, unlike the case for local variables, the variables
of the return expression cannot be referred to and so the typeof operator
would need obscure dummy arguments of the same type added (see example above
where default constructed temporaries are used; in fact this has a problem
where the unknown type is not default constructible, requiring kludges like
casting a null pointer to the required type).
(3) In the case where several function return paths return different,
convertible, types it is not clear which expression should be used in the
return typeof.
(4) For the return type, although the majority of cases in which typeof is
needed lead to small expressions which may be manageable, in principle one
can construct cases where essentially the whole of the function body would
be need to be listed in the return typeof . As well as being very difficult
to read, how to handle multi-statement sequences is unclear.
(5) The final result is syntactically ugly and difficult to write and read;
it essentially adds code, in the form of the return expression, to the
function prototype.
Proposed solution by extending C++ type deduction:
The fundamental problem is that L and R in the above template function are
essentially non-deduced contexts. A solution, then is to specify that after
function argument deduction, if function template parameters remain
non-deduced and are not used as part of the function signature (excluding
the return type), then the type is deduced to be that of the initializing
expression (or more accurately, it is deduced by the same algorithm that a
function parameter is deduced to match an initialising function argument).
So, for example, L above will be deduced to be the type of the initializing
expression "p1 * p2", and R will be deduced as the type of the return
expression "local * p2".
A difficulty with the return type deduction is that there can be multiple
return paths from a function. If this is the case then the type R is deduced
as the widest type of all return paths (this is the same rule used for the
return type of the conditional operator (?:) ). If there is no single
convertible type then, of course, the deduction fails.
At first glance, it may appear that a problem with this solution is that the
function source code needs to be available to the compiler to deduce the
function prototype when the template is instantiated. But this is, of
course, a requirement for a template function anyway, and, as discussed
above, using typeof for the return type essentially requires the user to put
a large part (potentially all) of a function's implementation in the
function prototype in type form anyway.
Note: it may also be argued that a minor disadvantage of this solution is in
the usage of typeof in a non-template function such as the following
void func()
{
.
typeof(compose(f1, f2)) ff = compose(f1, f2);
.
}
(Such a usage, although strictly unnecessary as all types are in principal
known, may still be convenient if the type if large and unwieldy as a return
type from a compose operator often is)
The proposed extended type-deduction solution would require func( ) to
become a template function, with the attendant compile-time costs of
template functions
template<typename L>
void func()
{
.
L ff = compose(f1, f2);
.
}
However, a good implementation of export could detect this case and
essentially treat it as a non-template function (i.e. create a fully
compiled instantiation).
In summary, the advantages of this extension to the C++ type deduction
mechanism include-
(1) Fully backward compatible- it simply allows code to compile that would
previously fail to compile.
(2) No new keyword needed.
(3) Adds minimal verbiage compared to the use of typeof, and has a
straightforward interpretation that cannot be accidentally used incorrectly.
Case Studies:
(I) Maximum function
The first case study is implementing a max( ) function that can take any
convertible arguments e.g. int and double
The standard library version of max( )
template<typename T>
T std::max(T t1, T t2 )
{
if (t1 > t2)
return t1;
else
return t2;
}
fails for different convertible types as t1 and t2 must be exactly the same
type.
A first attempt using typeof:
template<typename T1, typename T2>
typeof(???) std::max(T1 t1, T2 t2 )
{
if (t1 > t2)
return t1;
else
return t2;
}
but it is not clear what the return type should be.
Using a conditional expression converts to a common type:
template<typename T1, typename T2>
typeof((T1() > T2()) ? T1() : T2();) std::max(T1 t1, T2 t2 )
{
return (t1 > t2) ? t1 : t2;
}
The solution using extended type inference is straightforward and obvious:
template<, typename R, typename T1, typename T2 >
R std::max(T1 t1, T2 t2 )
{
if (t1 > t2)
return t1;
else
return t2;
}
(Note: for this example it is, in fact, possible to write a traits class
convert_type<T1, T2>::convert_t, that allows the lookup of the common
conversion type of two types, so it could be used in this example instead of
a full typeof implementation, but it is still fundamentally a kludge and
more verbose than the second example above.)
(II) Function composition
A function object composition operator looks something like the following-
template<typename F1, typename F2>
compose_func_object<F1, F2> compose(F1 f1, F2 f2)
{
return compose_func_object<F1, F2>(f1, f2);
}
where compose_func_object is a new function object that stores the original
function objects and computes their composition.
Implementing compose_func_object requires deducing the function objects's
return type when it is called.
The standard library solution is to require function objects to redundantly
include member typedefs describing the return type
template<typename F1, typename F2>
struct compose_func_obj
{
typedef typename F1::return_type return_type;
..
template<typename P>
typename F1::return_type operator()(P p)
{
return m_f1(m_f2(p));
}
..
};
With a typeof operator, the redundant type information is not needed, but
still the clumsy redundant syntax is required which could harbour *very*
obscure bugs if the type expression is incorrect.
template<typename F1, typename F2>
struct compose_func_obj
{
template< typename P>
typeof( F2()(F1()) ) operator()(P p)
{
return m_f1(m_f2(p));
}
..
};
// oops.. that should have been "typeof( F1()(F2()) )"
template<typename F1, typename F2>
struct compose_func_obj
{
template< typename P>
typeof( F1()(F2()) ) operator()(P p)
{
return m_f1(m_f2(p));
}
..
};
The solution using extended type inference is direct, with no possibility of
an incorrect return type being specified.
template<typename F1, typename F2>
struct compose_func_obj
{
template<typename R, typename P>
R operator()(P p)
{
return m_f1(m_f2(p));
}
..
};
Proposed change to text of standard:
14.8 [temp.deduct]
Add the following text-
"
Deducing local variables and function return type
If a template parameter P is not deduced from a function call (14.8.2.1) and
is not explicitly specified (14.8.1) then it is deduced as per (14.8.2.1)
where the argument type A is taken to be the type of the initializing
expression. If there is no initializing expression, then it is a non-deduced
context. For a function return template parameter, the argument type A is
taken to be the widest type of the return expression(s) of the function
where the widest type is deduced as per the conditional expression 5.16
[exp.cond].
"
(this wording could obviously be improved, and the text from the conditional
operator conversion to widest type algorithm could be inserted inline).
Conclusion:
As far as I am aware, this proposed extension to the type deduction
mechanism of C++ provides all of the power of typeof in a much more
straightforward and cleaner manner. I would be very interested to hear of
any examples where typeof is needed that cannot be handled by this extended
type inference proposal.
Also, I would like to hear of any comments on any fundamental implementation
difficulties or other problems with the suggested scheme.
,Brian Parker
(brianjparker@ozemail.com.au)
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Anthony Williams" <anthwil@nortelnetworks.com>
Date: Mon, 12 Nov 2001 11:20:28 GMT Raw View
"Brian Parker" <brianjparker@ozemail.com.au> wrote in message
news:fTlH7.2855$c87.79835@ozemail.com.au...
[snip]
> template<typename R, typename P1, typename P2, typename L>
> R func(P1 p1, P2 p2)
> {
> L local = p1 * p2;
>
> return local * p2;
> }
[snip]
> Proposed solution by extending C++ type deduction:
> The fundamental problem is that L and R in the above template function are
> essentially non-deduced contexts. A solution, then is to specify that
after
> function argument deduction, if function template parameters remain
> non-deduced and are not used as part of the function signature (excluding
> the return type), then the type is deduced to be that of the initializing
> expression (or more accurately, it is deduced by the same algorithm that a
> function parameter is deduced to match an initialising function argument).
>
> So, for example, L above will be deduced to be the type of the
initializing
> expression "p1 * p2", and R will be deduced as the type of the return
> expression "local * p2".
This would break existing template code, which allows L and R to be
non-deduced, and explicitly specified. This is a feature used in real code.
I agree that typeof() can be clumsy, but this is not a viable alternative.
Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Brian Parker" <brianjparker@ozemail.com.au>
Date: Mon, 12 Nov 2001 21:48:03 GMT Raw View
"Anthony Williams" <anthwil@nortelnetworks.com> wrote in message
news:9so3n6$14m2on$1@ID-49767.news.dfncis.de...
> "Brian Parker" <brianjparker@ozemail.com.au> wrote in message
> news:fTlH7.2855$c87.79835@ozemail.com.au...
> This would break existing template code, which allows L and R to be
> non-deduced, and explicitly specified. This is a feature used in real
code.
>
> I agree that typeof() can be clumsy, but this is not a viable alternative.
This proposed mechanism would only be used if the template arguments were
not deduced *or explicitly specified*.
In the proposed wording for the standard I say "If a template parameter P is
not deduced from a function call (14.8.2.1) *and
is not explicitly specified* (14.8.1) then ..."
Sorry, for not making that clearer. This extension would not break existing
code, as far as I can see.
Thaks for your comments,
Brian Parker
(brianjparker@ozemail.com.au)
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Tue, 13 Nov 2001 00:56:05 GMT Raw View
anthwil@nortelnetworks.com (Anthony Williams) wrote (abridged):
> This would break existing template code, which allows L and R to be
> non-deduced, and explicitly specified. This is a feature used in real
> code.
Presumably explicit instantiation would still be permitted. Eg calls like:
double x = func<int,int,double,double>( 1, 2 );
would compile with the same semantics as now. Only types not specified
explicitly and not deduced from arguments would be deduced from their
initialising expression. It would be a last resort.
I have to say, the phrase "last resort" is very disconcerting when used in
conjunction with name look-up rules.
Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
brangdon@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Tue, 13 Nov 2001 00:56:18 GMT Raw View
brianjparker@ozemail.com.au (Brian Parker) wrote (abridged):
> A solution, then is to specify that after function argument
> deduction, if function template parameters remain non-deduced and
> are not used as part of the function signature (excluding
> the return type), then the type is deduced to be that of the
> initializing expression (or more accurately, it is deduced by the
> same algorithm that a function parameter is deduced to match
> an initialising function argument).
Interesting idea.
What happens with recursive loops?
export template <typename R, typename P1>
R func2( P1 p1 );
export template <typename R, typename P1>
R func1( P1 p1 ) {
return func2( p1 );
}
export template <typename R, typename P1>
R func2( P1 p1 ) {
return func1( p1 );
}
What is the type of func1(1)? Is the compiler required to give a
diagnostic? If so, when?
What happens with "export"? The standard says in $14.8:
[Note: an implementation may require that a translation unit
containing the definition of an exported template be compiled
before any translation unit containing an instantiation of
that template.]
When is the definition of func1() complete? May the compiler require to
see the definition of func2() before func1() can be instantiated?
Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
brangdon@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: Michiel Salters<Michiel.Salters@cmg.nl>
Date: Tue, 13 Nov 2001 01:44:00 GMT Raw View
In article <fTlH7.2855$c87.79835@ozemail.com.au>, Brian Parker says...
>
>Hi all,
>
>The following is a proposal I have been considering that gives the benefits
>of typeof with only minor extensions to the current C++ type deduction
>rules...
>Introduction:
>
>The type deduction mechanism of C++98 has several limitations that
>unnecessarily limit aspects of generic programming. As a solution for these
>limitations, it has often been proposed that a typeof() operator be added as
>a language extension. In this paper, it is shown that typeof(), whilst
>solving most such problems, is syntactically clumsy and has its own
>limitations. As an alternative, the semantics of type deduction in C++ can
>be extended without adding a new keyword to produce a more powerful, more
>elegant, and easier to use solution. If this extension was implemented, the
>language would have much more complete support for generic programming.
[ SNIP typeof explanation ]
>Proposed solution by extending C++ type deduction:
>The fundamental problem is that L and R in the above template function are
>essentially non-deduced contexts. A solution, then is to specify that after
>function argument deduction, if function template parameters remain
>non-deduced and are not used as part of the function signature (excluding
>the return type), then the type is deduced to be that of the initializing
>expression (or more accurately, it is deduced by the same algorithm that a
>function parameter is deduced to match an initialising function argument).
>
>So, for example, L above will be deduced to be the type of the initializing
>expression "p1 * p2", and R will be deduced as the type of the return
>expression "local * p2".
>A difficulty with the return type deduction is that there can be multiple
>return paths from a function. If this is the case then the type R is deduced
>as the widest type of all return paths (this is the same rule used for the
>return type of the conditional operator (?:) ). If there is no single
>convertible type then, of course, the deduction fails.
>
>At first glance, it may appear that a problem with this solution is that the
>function source code needs to be available to the compiler to deduce the
>function prototype when the template is instantiated. But this is, of
>course, a requirement for a template function anyway, and, as discussed
>above, using typeof for the return type essentially requires the user to put
>a large part (potentially all) of a function's implementation in the
>function prototype in type form anyway.
You are wrong. A template can be exported, in which case the source code need
not be available where the template is declared.
>Note: it may also be argued that a minor disadvantage of this solution is in
>the usage of typeof in a non-template function such as the following
>
>void func()
>{
> .
> typeof(compose(f1, f2)) ff = compose(f1, f2);
> .
>}
>
>(Such a usage, although strictly unnecessary as all types are in principal
>known, may still be convenient if the type if large and unwieldy as a return
>type from a compose operator often is)
In addition, it is a major disadvantage that this solution does not work
in class templates. Many of the uses of typeof are not associated with
an initializing expression in such context, but are used to define
class members.
>In summary, the advantages of this extension to the C++ type deduction
>mechanism include-
>(1) Fully backward compatible- it simply allows code to compile that would
>previously fail to compile.
>(2) No new keyword needed.
>(3) Adds minimal verbiage compared to the use of typeof, and has a
>straightforward interpretation that cannot be accidentally used incorrectly.
Let's see if (3) holds
template <typename Local, typename Arg>
void f( Arg a )
{
Local i=a, j=a+1;
//...
}
Oops. Is typeof(a)==typeof(a+1) ? If not ?
I think this proposal needs some work to handle class templates, exported
templates and a cleaner formulation of "THE type of initializing expression"
Regards,
Michiel Salters
--
Michiel Salters
Consultant Technical Software Engineering
CMG Trade, Transport & Industry
Michiel.Salters@cmg.nl
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: pdimov@mmltd.net (Peter Dimov)
Date: Tue, 13 Nov 2001 01:44:49 GMT Raw View
"Brian Parker" <brianjparker@ozemail.com.au> wrote in message news:<fTlH7.2855$c87.79835@ozemail.com.au>...
[...]
> template<typename R, typename P1, typename P2, typename L>
> R func(P1 p1, P2 p2)
> {
> L local = p1 * p2;
>
> return local * p2;
> }
[...]
> Proposed solution by extending C++ type deduction:
> The fundamental problem is that L and R in the above template function are
> essentially non-deduced contexts. A solution, then is to specify that after
> function argument deduction, if function template parameters remain
> non-deduced and are not used as part of the function signature (excluding
> the return type), then the type is deduced to be that of the initializing
> expression (or more accurately, it is deduced by the same algorithm that a
> function parameter is deduced to match an initialising function argument).
This makes the function signature depend on the definition. If you
replace the definition with a declaration:
template<typename R, typename P1, typename P2, typename L>
R func(P1 p1, P2 p2);
R and L can no longer be deduced.
[...]
> Although this solves the main problem, there are several problems with
> typeof-
>
> (1) For local variables it is unnecessarily verbose- it redundantly repeats
> the initializing expression, leading to the possibility of obscure errors
> (which would be difficult to track down as the final compile-time types
> would not be visible).
Correct. typeof() should not target this problem. It's better solved
by using a helper function template or by introducing a separate "let
x = expr" syntax.
> (2) For the return type, unlike the case for local variables, the variables
> of the return expression cannot be referred to and so the typeof operator
> would need obscure dummy arguments of the same type added (see example above
> where default constructed temporaries are used; in fact this has a problem
> where the unknown type is not default constructible, requiring kludges like
> casting a null pointer to the required type).
Also correct but we'll probably have to live with it.
--
Peter Dimov
Multi Media Ltd.
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Anthony Williams" <anthwil@nortelnetworks.com>
Date: Tue, 13 Nov 2001 19:21:28 GMT Raw View
"Brian Parker" <brianjparker@ozemail.com.au> wrote in message
news:3GVH7.4669$c87.134436@ozemail.com.au...
> "Anthony Williams" <anthwil@nortelnetworks.com> wrote in message
> news:9so3n6$14m2on$1@ID-49767.news.dfncis.de...
> > "Brian Parker" <brianjparker@ozemail.com.au> wrote in message
> > news:fTlH7.2855$c87.79835@ozemail.com.au...
>
> > This would break existing template code, which allows L and R to be
> > non-deduced, and explicitly specified. This is a feature used in real
> code.
> >
> > I agree that typeof() can be clumsy, but this is not a viable
alternative.
>
> This proposed mechanism would only be used if the template arguments were
> not deduced *or explicitly specified*.
> In the proposed wording for the standard I say "If a template parameter P
is
> not deduced from a function call (14.8.2.1) *and
> is not explicitly specified* (14.8.1) then ..."
>
> Sorry, for not making that clearer. This extension would not break
existing
> code, as far as I can see.
It might make code that should be an error compile cleanly. Also, how would
it interact with overloaded templates?
template<typename T>
int someFunc(const T&);
template<typename T,typename R>
R someFunc(const T& t)
{
return t;
}
int i=someFunc(3); // someFunc<int> or someFunc<int,int>?
template<typename T,typename R>
R castTo(const T& t)
{
return t;
}
int i=castTo(25); // should be error, now will compile OK.
Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Brian Parker" <brianjparker@ozemail.com.au>
Date: Tue, 13 Nov 2001 19:21:40 GMT Raw View
"Dave Harris" <brangdon@cix.co.uk> wrote in message
news:memo.20011112225724.3923D@brangdon.madasafish.com...
> brianjparker@ozemail.com.au (Brian Parker) wrote (abridged):
> > A solution, then is to specify that after function argument
> > deduction, if function template parameters remain non-deduced and
> > are not used as part of the function signature (excluding
> > the return type), then the type is deduced to be that of the
> > initializing expression (or more accurately, it is deduced by the
> > same algorithm that a function parameter is deduced to match
> > an initialising function argument).
>
> Interesting idea.
>
> What happens with recursive loops?
>
> export template <typename R, typename P1>
> R func2( P1 p1 );
>
> export template <typename R, typename P1>
> R func1( P1 p1 ) {
> return func2( p1 );
> }
>
> export template <typename R, typename P1>
> R func2( P1 p1 ) {
> return func1( p1 );
> }
>
> What is the type of func1(1)? Is the compiler required to give a
> diagnostic? If so, when?
Yes, good point- I hadn't considered such mutually recursive functions.
Although surely typeof() would also have trouble with this situation...
export template <typename P1>
typeof(????) func2( P1 p1 );
export template <typename P1>
typeof(func2(P1())) func1( P1 p1 ) {
return func2( p1 );
}
export template <typename P1>
typeof(func1(P1())) func2( P1 p1 ) {
return func1( p1 );
}
Agreed, the treatment of case would need to be specified, if only to say
that the behaviour is unspecified.
> What happens with "export"? The standard says in $14.8:
>
> [Note: an implementation may require that a translation unit
> containing the definition of an exported template be compiled
> before any translation unit containing an instantiation of
> that template.]
>
> When is the definition of func1() complete? May the compiler require to
> see the definition of func2() before func1() can be instantiated?
The simplest solution would just to disallow the type deduction in this
case, although I agree that that the exact wording would be tricky.
I can't think of a simple wording at the moment but I suspect (hope) that it
could be nailed down as it is just the same situation where typeof would
fail.
I'll have to think about this one some more, but I don't think it is a
show-stopper...
,Brian Parker
---
[ 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://www.research.att.com/~austern/csc/faq.html ]
Author: "Brian Parker" <brianjparker@ozemail.com.au>
Date: Tue, 13 Nov 2001 19:21:49 GMT Raw View
"Michiel Salters" <Michiel.Salters@cmg.nl> wrote in message
news:58LH7.21413$xS6.32670@www.newsranger.com...
>...
> You are wrong. A template can be exported, in which case the source code
need
> not be available where the template is declared.
I was aware of export. An environment supporting export must have some
mechanism to find the exported pre-compiled templates when it needs to
instantiate them.
I presumed that an environment supporting export could use this mechanism to
also lookup the deduced function return type.
However, as was pointed out in a previous response, the standard says:
[Note: an implementation may require that a translation unit
containing the definition of an exported template be compiled
before any translation unit containing an instantiation of
that template.]
So if an implementation required this then I don't think there would be a
problem. But I agree that an implementation that didn't require this may
make implementing this proposal for exported functions difficult (or
impossible?).
One simple possibility would be to limit this proposal to non-exported
functions, but this would severely reduce the utility of it.
I don't know enough about what extra-linguistic support export requires to
be able to say definitively whether it is an insurmountable problem for this
proposal; hopefully implementations supporting export will be available in
the not-too-distant future.
>
> In addition, it is a major disadvantage that this solution does not work
> in class templates. Many of the uses of typeof are not associated with
> an initializing expression in such context, but are used to define
> class members.
Hmmm... this could be a major limitation of this proposal (or at least might
suggest that typeof( ) would still be useful in addition to this proposal).
Do you have a specific example where a class member type would need type
typeof()?
>
> Let's see if (3) holds
>
> template <typename Local, typename Arg>
> void f( Arg a )
> {
> Local i=a, j=a+1;
> //...
> }
>
> Oops. Is typeof(a)==typeof(a+1) ? If not ?
Yes, this edge case would need to be specified, probably by specifying that
i is used for type deduction in the above example.
> I think this proposal needs some work to handle class templates, exported
> templates and a cleaner formulation of "THE type of initializing
expression"
Agreed, though the class template issue and possible the export issue may be
show-stoppers. I would be very interested in specific class examples where
typeof() would still be needed.
Thanks,
Brian Parker.
---
[ 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://www.research.att.com/~austern/csc/faq.html ]