Boost logo

Boost :

From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2002-02-21 09:55:45


Disclaimer: I know I should have been participating in c.l.cpp.m and c.s.cpp
for this auto issue before. I'm always too busy, and given the high flow
there, I usually just read, from time to time, those ngs, but scarcely
participate.
This auto issue appears to have evolved already, but...

More and more I'm getting convinced that 'auto', as it is being currently
shaped, is Evil.
I would much prefer a more expressive type deduction scheme.

Consider this:

int foo();
double bar();
std::string fred();
MyFancyClass wilma() ;
void glue(int,double,std::string,MyFancyClass);

int can_you_guess_what_is_this_doing()
{
  auto a = foo();
  auto b = bar();
  auto c = fred();
  auto d = wilma() ;

  if ( a + b < static_cast<auto>(d) ) // (*1)
  {
    c = c + 1 ;
    auto e = a - b ;
    if ( e < bar() )
    {
       glue(a,b,c,d);
    }
  }
}

Now, the program above will compile and run. But it is a maintenance
headache.

(*1) // I would cast 'd' to a 'know' type, which I don't know exactly here,
so I just
     // trust that a cast to auto would do the job

The main problem, IMO, is that all variables are 'syntactically' typeless.
That is, they do have type, but they don't express it, so building up
expressions such as ( a + b < static_cast<...>(d) ), or (c = c + 1 ) is too
error prone.

I feel impelled to INSIST that non-expressive type deductions should be
abandoned.
We need to figure out how to make it more expressive.

I know I've said this much for auto return types, but now I'm getting
paranoid about bare 'auto's anywhere else.

The problem, IMO, is that a very important feature of a type is its name.
With type-NAMES, I can look at a piece of code and recognize possible
interactions inside expressions, the correct order of arguments, etc...
Even though I won't know what exactly is a deduced-type, by giving it a name
I would at least differentiate it from other types.
IOW, when I write:

 template<class T, class U> bool less( T t, U u ) { return t < u ; }

I know that t and u might be of different types, in fact, I would pretend
they are.

I'm on the numerical field, and to us, being able -at least- to know that
the types participating in an expression are different, even if we don't
know exactly what they are and how they interact, is really fundamental.
This is all lost with a non-expressive auto as the one being coined.

I can think of syntactic addition to auto that would at least give it a
little expressiveness. Perhaps this has been discussed before and ruled out.
In that case, I'd like to be referred to the relevant messages.

int can_you_guess_what_is_this_doing()
{
  auto T a = foo();
  auto U b = bar();
  auto K c = fred();
  auto L d = wilma() ;

  if ( a + b < static_cast<typeof(T+U)>(d) ) // (*2)
  {
    c = c + static_cast<K>(1) ; (*3)
    auto M e = a - b ; (*4)
    if ( e < bar() )
    {
       glue(a,b,c,d);
    }
  }
}

The idea is that auto actually introduces a type, with a name like any other
type.
This isn't much different than using the template syntax instead of 'auto'
as others suggested;

My point in all this message is that a key feature of any type-deduction
scheme is that it should introduce a NAME.

The idea is that auto can only be used to conveniently declare a new type
from which I HAVE TO give it a NAME.
There would be rules for named type declarations using auto, which I show
with examples:

auto T a = foo() ; // this declares a type-alias 'T' for 'int'.

T b = 2 ; // OK. T is an alias for int

auto T b = foo(); // OK. auto is allowed to redeclare the same type-alias
                  // as long as it maps to the same type.

T a = MyFancyClass() ; // error, cannot convert MyFancyClass to T(int)

auto T c = wilma() ; // error, re-declaration of type-alias T mismatch.
'MyFancyClass' is not 'int'.

auto T d = bar() ; // error, re-declararation of type-alias T mismatch.
'double' is not 'int'.

T e = bar() ; // OK. T is int, so the assignment involves an implicit
conversion from 'double'.

Remarks on the sample code:

(*2) Since 'a' and 'b' are of types T and U (declared by auto), even though
I don't know what T and U really are, it helps me figure out how to cast
'd' for the comparison. In this case, since I'm adding values of types T
and U, I use typeof(T+U).

(*3) Here, I now that c is of type K so I know that I can cast (1) to K.

(*4) Here, (a-b) involves different types (I know they are different since
they are of type T and U).
     I can asumme in advance that the type of the expression ought to be
typeof(T-U), and write: typeof(T-U) e = (a-b);
     But I can also use auto with a new type M.
     (I could even check if M is either T or U as it would be expected)

Conclusion:

We should think of an auto-type-deduction scheme that gives deduced-types
distinct names.
So instead of: auto a = expr ;
We should have: auto T a = exp ;

(Or: template<class T> T a = exp; for that matter)

Fernando Cacciola
Sierra s.r.l.
fcacciola_at_[hidden]
www.gosierra.com


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk