Topic: A thought about "auto" from N1607 as function parameter


Author: rtw@freenet.co.uk (Rob Williscroft)
Date: Thu, 29 Jul 2004 04:50:03 GMT
Raw View
David Olsen wrote in news:2mos3kFle8s7U1@uni-berlin.de in comp.std.c++:

> It took me a while, but I finally figured out what your complaint is,

Its not a complaint really, just my paranoia :)

> and I think it is valid.  Unfortunately, the problem is hard to
> explain. But let me take a shot.
>
> Some user-defined types are meant to be used only for temporary
> objects. They may be a nested private class or a member of an inner
> namespace (such as "detail") so that users would have a very hard time
> accidentally creating variables of that type. ...

More importantly they are not documented. In that sense auto is a major
shift in the way C++ programmers (particularly library authors) need
to write code.

> ... They usually have an implicit conversion operator to a more
> "permanent" type and/or an overloaded assignment operator.  Because
> objects of the type are always supposed to be temporaries and are
> never supposed to survive beyond the end of the expression, the object
> may store references or pointers to other temporary objects.  In the
> above example, etl::detail::plus_temp<L,R> is such a type.
>
> But the "auto" declaration proposal gives users an easy way to
> accidentally declare variables of such types, since they don't need to
> know the name of the type.  "auto" makes it easy to use such types in
> ways that were not intended, resulting in undefined behavior.
>
> Here is a shorter, but more contrived, example.  If the first call to
> push_back causes a reallocation, then the second call may blow up,
> depending on how vector<bool>'s proxy type is implemented.
>
> template <typename C> void copy_begin_to_end_twice(C &c)
> {
>     auto e = c[0];
>     c.push_back(e);
>     c.push_back(e);
> }
>
> void f()
> {
>      std::vector<bool> vb; /* fill vb with a bunch of values */
>      copy_begin_to_end_twice(vb);
> }
>

Nice example, it leads me to yet another possible solution:

struct vector_bool_proxy
{
  auto operator bool () { /* usual operator bool here */ }
  // or maybe
  bool operator auto () { return bool( *this ); }
  // or
  operator auto bool () { ... }
};

I prefer the second/third as it seperates auto from the normal conversion
operator (and the type could be the result of a meta-function :).

It still leaves the problem that currently working code needs to be
rewriten to be auto-aware.

> I haven't studied N1607, so I don't know if it addresses this problem,
> or what should be done if it doesn't.
>

AFAICT it doesn't mention the problem (I've read it at least twice),
and even if its decided nothing should be done about it, it should
(IMO) at least mention it.

Rob.
--
http://www.victim-prime.dsl.pipex.com/

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: sarima@friesen.net (Stanley Friesen)
Date: Thu, 29 Jul 2004 15:58:57 GMT
Raw View
qg4h9ykc5m@yahoo.com (David Olsen) wrote:
>
>It took me a while, but I finally figured out what your complaint is,
>and I think it is valid.  Unfortunately, the problem is hard to explain.
>But let me take a shot.
>
>Some user-defined types are meant to be used only for temporary objects.
>They may be a nested private class or a member of an inner namespace
>(such as "detail") so that users would have a very hard time
>accidentally creating variables of that type.  They usually have an
>implicit conversion operator to a more "permanent" type and/or an
>overloaded assignment operator.  Because objects of the type are always
>supposed to be temporaries and are never supposed to survive beyond the
>end of the expression, the object may store references or pointers to
>other temporary objects.  In the above example,
>etl::detail::plus_temp<L,R> is such a type.
>
>But the "auto" declaration proposal gives users an easy way to
>accidentally declare variables of such types, since they don't need to
>know the name of the type.  "auto" makes it easy to use such types in
>ways that were not intended, resulting in undefined behavior.

Possible partial solution: restrict "auto" to declaring only accessible
types.  Then a private type, at least, would not be usable by this path.

--
The peace of God be with you.

Stanley Friesen

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: qg4h9ykc5m@yahoo.com (David Olsen)
Date: Fri, 30 Jul 2004 14:49:54 GMT
Raw View
Stanley Friesen wrote:
> qg4h9ykc5m@yahoo.com (David Olsen) wrote:
>>But the "auto" declaration proposal gives users an easy way to
>>accidentally declare variables of such types, since they don't need to
>>know the name of the type.  "auto" makes it easy to use such types in
>>ways that were not intended, resulting in undefined behavior.
>
>
> Possible partial solution: restrict "auto" to declaring only accessible
> types.  Then a private type, at least, would not be usable by this path.

But accussibility applies to names, not types.  Through typedefs, a type
can have many different names, some of which may be accessible and some
of which may not.  There is no way for "auto" to know which name would
have been used to refer to the type in that place in the code, so it
doesn't know which name to check.

--
David Olsen
qg4h9ykc5m@yahoo.com

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: rtw@freenet.co.uk (Rob Williscroft)
Date: Sat, 24 Jul 2004 20:06:13 GMT
Raw View
Daniel Frey wrote in news:cdqnf4$fi9$1@swifty.westend.com in
comp.std.c++:

> Hello world,
>
> I thought about the N1607-proposal for decltype and auto. One of the
> proposed additions is, that "auto" can be used for function
> parameters. The suggestion is to make it an implicit template
> parameter. I wonder if this is a good idea and what consequences would
> follow. Consider:
>
> void f( auto i ); // Forward declaration

If we have an implicit template paramiter don't we have:

template< typename _DEDUCED_ > void f( _DEDUCED_ i );

I.e. the same thing as below.

> template< typename T > void f( T i ); // Forward declaration
>
> template< typename U > void f( U i ) {} // Definition of which f?
>
> int main() { f( 42 ); }
>
> The above shows my problem: Which function is taken?

There is only one function.

> Is this an
> ambiguous example? Or is it a double forward declaration of the *same*
> function, which is then defined. The proposal's text on page 21 makes
> me think it's the latter, but was this intended?

Intention's don't matter, the text makes them equivalent, its either
good or bad, I think good (FWIW) in this case.

>
> The next problem is, that an "implicit template parameter" is
> confusing for users. They don't see the word "template", so errors are
> ways more likely to occur in their cognition of the function's type
> (IMHO).

Won't auto have the same effect. Note 8.2 Removing auto Storage specifier
on page 17, so auto will mean deduce-the-type and nothing else.

>
> It seems to me that the introduction of "implicit template parameters"
> is the wrong way, even more so as it doesn't offer any benefit over
> template parameters. I thus suggest to either remove it completely
> (you can use a template parameter instead) or introduce a lookup-level
> in between "normal" function and template functions. This would mean
> for the above example:
>
> void f( auto i )
>
> has a different signature than
>
> template< typename T > void f( T i )
>
> and the first will be prefered over the latter, because it's not a
> template function. I don't have a concrete example where this could be
> useful, but it will allow an even more fine-grained control.

C++ just isn't confusing enough ?

> Of course
> mixed-functions that have both template and auto-parameters will have
> to be ordered, too (no, I won't provide details ;) and all this will
> be a lot of work, but without it, I think the current proposal of
> "auto" for function parameters is pretty pointless.

It saves typing (keyboard typing) and reduces "visual clutter", also
it has a certain symetry with the 'auto' used as the function return
type { auto f( auto, a, auto b ) -> decltype( a + b ); }.

>
> OTOH, allowing more fine-grained control over which function is taken
> might allow us to do things we can't even dream of today. But then,
> yes, the current rules for function overloading are complicated enough
> already...
>

No, that isn't the way to design a feature, "Lets have this it might have
some interesting side effects" (unless your names Dr Jekyll of course :)

I do think there is a sequence-point/UB problem with auto/decltype:

#include <iostream>
#include <ostream>

namespace etl
{
  struct value_type
  {
    int a, b;
    value_type( int aa, int bb ) : a( aa ), b( bb ) {}
  };

  namespace detail
  {
    template < typename L, typename R >
    struct plus_temp
    {
      L const *left;
      R const *right;
      operator value_type() const
      {
        value_type const &l = *left;
        value_type const &r = *right;
        return value_type( l.a + r.a, l.b - r.b );
      }
      plus_temp( L const &l, R const &r) :
          left( &l ), right( &r )
      {
      }
    };
  }

  template < typename L, typename R >
  inline detail::plus_temp< L, R >
    operator + ( L const &left, R const &right )
  {
    return detail::plus_temp< L, R >( left, right );
  }
}

#if !defined( USE_AUTO_DECLTYPE )

  /*  How to use the (etl) library
  */
  etl::value_type f( etl::value_type a, etl::value_type b )
  {
    return a + etl::value_type( 1, 2 ) + b;
  }

#else

  /*  How *not* to use the (etl) library
      - Note the sequence point (*)!
  */
  etl::value_type f( etl::value_type a, etl::value_type b )
  {
    auto temp = a + etl::value_type( 1, 2 ); /* <- (*) */
    return temp + b;
  }

#endif

int main()
{
  using namespace std;

  etl::value_type a( 1, 2 ), b( 3, 4 );
  etl::value_type r = f( a, b );

  cout << r.a << ", " << r.b << endl;
}

Ofcourse I could be missing something about how auto/decltype
relates to sequence points, if not, solution's could be:

1) Any temporary bound to a const & paramiter has its lifetime
   extended to the lifetime of the auto its initializing.

2) rvalue deduction (we may get this anyway) - a new version of
   etl above could use this to return a different (copy of the rvalue)
   return type.

3) A way of marking a type as not usable in an (non function paramiter)
   auto initialization:

     struct plus_temp !auto { /* ... */};

4) A way of marking an function return value as not usable in an
   (non function paramiter) auto initialization:

     !auto plus_temp< L, R > operator + ( /* ... */ ) { /* ... */ }

5) I've missed something ... (move symantics possibly) :)

(1) is the only one that won't cause problems for existing libraries.

I don't like (2) as it makes libraries harder to write (but rvalue
deduction would still be nice).

(3) & (4) are messy but otherwise Ok I guess.

Rob.
--
http://www.victim-prime.dsl.pipex.com/

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Mon, 26 Jul 2004 21:32:13 GMT
Raw View
Rob Williscroft wrote:
> Daniel Frey wrote in news:cdqnf4$fi9$1@swifty.westend.com in
> comp.std.c++:
>
>
> Ofcourse I could be missing something about how auto/decltype
> relates to sequence points, if not, solution's could be:
>
> 1) Any temporary bound to a const & paramiter has its lifetime
>    extended to the lifetime of the auto its initializing.

Well, it's already like this in C++. The lifetime of a temporary that is
bound to a reference persists for the lifetime of the reference. See
12.2/5 for details.

Alberto

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: rtw@freenet.co.uk (Rob Williscroft)
Date: Tue, 27 Jul 2004 03:32:49 GMT
Raw View
Alberto Barbati wrote in news:GpbNc.13024$1V3.317654@twister2.libero.it
in comp.std.c++:

> Rob Williscroft wrote:
>> Daniel Frey wrote in news:cdqnf4$fi9$1@swifty.westend.com in
>> comp.std.c++:
>>
>>
>> Ofcourse I could be missing something about how auto/decltype
>> relates to sequence points, if not, solution's could be:
>>
>> 1) Any temporary bound to a const & paramiter has its lifetime
>>    extended to the lifetime of the auto its initializing.
>
> Well, it's already like this in C++. The lifetime of a temporary that
> is bound to a reference persists for the lifetime of the reference.
> See 12.2/5 for details.

>From 12.2/5:

    ...
    A temporary bound to a reference parameter in a function call
    (5.2.2) persists until the completion of the full expression
    containing the call.
    ...

Demo programme:

    #include <iostream>

    struct temp
    {
      ~temp() { std::cerr << "~temp()\n"; }
    };

    struct auto_t
    {
      temp const &ref;
      auto_t( temp const &_temp ) : ref( _temp ) {}
      ~auto_t() { std::cerr << "~auto_t()\n"; }

      void f() { std::cerr << "f()\n"; }
    };

    int main()
    {
      auto_t a = temp();
      a.f();
    }

Rob.
--
http://www.victim-prime.dsl.pipex.com/

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: qg4h9ykc5m@yahoo.com (David Olsen)
Date: Wed, 28 Jul 2004 05:59:17 GMT
Raw View
Rob Williscroft wrote:
> I do think there is a sequence-point/UB problem with auto/decltype:
>
> #include <iostream>
> #include <ostream>
>
> namespace etl {
>   struct value_type {
>     int a, b;
>     value_type( int aa, int bb ) : a( aa ), b( bb ) {}
>   };
>
>   namespace detail {
>     template < typename L, typename R >
>     struct plus_temp {
>       L const *left;
>       R const *right;
>       operator value_type() const {
>         value_type const &l = *left;
>         value_type const &r = *right;
>         return value_type( l.a + r.a, l.b - r.b );
>       }
>       plus_temp( L const &l, R const &r) :
>           left( &l ), right( &r )
>       { }
>     };
>   }
>   template < typename L, typename R >
>   inline detail::plus_temp< L, R >
>     operator + ( L const &left, R const &right )
>   {
>     return detail::plus_temp< L, R >( left, right );
>   }
> }
>
> #if !defined( USE_AUTO_DECLTYPE )
>
>   /*  How to use the (etl) library */
>   etl::value_type f( etl::value_type a, etl::value_type b ) {
>     return a + etl::value_type( 1, 2 ) + b;
>   }
>
> #else
>
>   /*  How *not* to use the (etl) library
>       - Note the sequence point (*)! */
>   etl::value_type f( etl::value_type a, etl::value_type b ) {
>     auto temp = a + etl::value_type( 1, 2 ); /* <- (*) */
>     return temp + b;
>   }
>
> #endif
>
> int main() {
>   using namespace std;
>
>   etl::value_type a( 1, 2 ), b( 3, 4 );
>   etl::value_type r = f( a, b );
>
>   cout << r.a << ", " << r.b << endl;
> }

It took me a while, but I finally figured out what your complaint is,
and I think it is valid.  Unfortunately, the problem is hard to explain.
But let me take a shot.

Some user-defined types are meant to be used only for temporary objects.
They may be a nested private class or a member of an inner namespace
(such as "detail") so that users would have a very hard time
accidentally creating variables of that type.  They usually have an
implicit conversion operator to a more "permanent" type and/or an
overloaded assignment operator.  Because objects of the type are always
supposed to be temporaries and are never supposed to survive beyond the
end of the expression, the object may store references or pointers to
other temporary objects.  In the above example,
etl::detail::plus_temp<L,R> is such a type.

But the "auto" declaration proposal gives users an easy way to
accidentally declare variables of such types, since they don't need to
know the name of the type.  "auto" makes it easy to use such types in
ways that were not intended, resulting in undefined behavior.

Here is a shorter, but more contrived, example.  If the first call to
push_back causes a reallocation, then the second call may blow up,
depending on how vector<bool>'s proxy type is implemented.

template <typename C> void copy_begin_to_end_twice(C &c) {
     auto e = c[0];
     c.push_back(e);
     c.push_back(e);
}
void f() {
     std::vector<bool> vb;
     /* fill vb with a bunch of values */
     copy_begin_to_end_twice(vb);
}

I haven't studied N1607, so I don't know if it addresses this problem,
or what should be done if it doesn't.

--
David Olsen
qg4h9ykc5m@yahoo.com

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: daniel.frey@aixigo.de (Daniel Frey)
Date: Fri, 23 Jul 2004 22:46:00 GMT
Raw View
Hello world,

I thought about the N1607-proposal for decltype and auto. One of the=20
proposed additions is, that "auto" can be used for function parameters.=20
The suggestion is to make it an implicit template parameter. I wonder if=20
this is a good idea and what consequences would follow. Consider:

void f( auto i ); // Forward declaration
template< typename T > void f( T i ); // Forward declaration

template< typename U > void f( U i ) {} // Definition of which f?

int main() { f( 42 ); }

The above shows my problem: Which function is taken? Is this an=20
ambiguous example? Or is it a double forward declaration of the *same*=20
function, which is then defined. The proposal's text on page 21 makes me=20
think it's the latter, but was this intended?

The next problem is, that an "implicit template parameter" is confusing=20
for users. They don't see the word "template", so errors are ways more=20
likely to occur in their cognition of the function's type (IMHO).

It seems to me that the introduction of "implicit template parameters"=20
is the wrong way, even more so as it doesn't offer any benefit over=20
template parameters. I thus suggest to either remove it completely (you=20
can use a template parameter instead) or introduce a lookup-level in=20
between "normal" function and template functions. This would mean for=20
the above example:

void f( auto i )

has a different signature than

template< typename T > void f( T i )

and the first will be prefered over the latter, because it's not a=20
template function. I don't have a concrete example where this could be=20
useful, but it will allow an even more fine-grained control. Of course=20
mixed-functions that have both template and auto-parameters will have to=20
be ordered, too (no, I won't provide details ;) and all this will be a=20
lot of work, but without it, I think the current proposal of "auto" for=20
function parameters is pretty pointless.

OTOH, allowing more fine-grained control over which function is taken=20
might allow us to do things we can't even dream of today. But then, yes,=20
the current rules for function overloading are complicated enough already=
...

Comments?

Regards, Daniel

--=20
Daniel Frey

aixigo AG - financial solutions & technology
Schlo=DF-Rahe-Stra=DFe 15, 52072 Aachen, Germany
fon: +49 (0)241 936737-42, fax: +49 (0)241 936737-99
eMail: daniel.frey@aixigo.de, web: http://www.aixigo.de

The hacks that we write today become the bugs of tomorrow.


---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: bekkah@web.de (Helium)
Date: Sat, 24 Jul 2004 19:32:41 GMT
Raw View
> Hello world,
>
> I thought about the N1607-proposal for decltype and auto. One of the
> proposed additions is, that "auto" can be used for function parameters.
> The suggestion is to make it an implicit template parameter. I wonder if
> this is a good idea and what consequences would follow. Consider:
>
> void f( auto i ); // Forward declaration
> template< typename T > void f( T i ); // Forward declaration
>
> template< typename U > void f( U i ) {} // Definition of which f?
>
> int main() { f( 42 ); }
>
> The above shows my problem: Which function is taken? Is this an
> ambiguous example? Or is it a double forward declaration of the *same*
> function, which is then defined. The proposal's text on page 21 makes me
> think it's the latter, but was this intended?
>
> The next problem is, that an "implicit template parameter" is confusing
> for users. They don't see the word "template", so errors are ways more
> likely to occur in their cognition of the function's type (IMHO).


I think you think too much in C++. Have you ever used a language with
a powerfull type inference system like ML or Haskell. I think this is
exactly what is inteded by this proposal.


> It seems to me that the introduction of "implicit template parameters"
> is the wrong way, even more so as it doesn't offer any benefit over
> template parameters. I thus suggest to either remove it completely (you
> can use a template parameter instead) or introduce a lookup-level in
> between "normal" function and template functions. This would mean for
> the above example:
>
> void f( auto i )
>
> has a different signature than
>
> template< typename T > void f( T i )
>
> and the first will be prefered over the latter, because it's not a
> template function. I don't have a concrete example where this could be
> useful, but it will allow an even more fine-grained control. Of course
> mixed-functions that have both template and auto-parameters will have to
> be ordered, too (no, I won't provide details ;) and all this will be a
> lot of work, but without it, I think the current proposal of "auto" for
> function parameters is pretty pointless.

Well you don't need for and while loops. All you need is if and goto.
If you have a compiler that supports tailrecursion you don't even need
goto. Anyway you'd like to have a while and a for in the language. I
think this is the same thing here with auto. of course you can emulate
it with templates, but ou still prefer the other way.

-- Matthias Becker

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]