auto for function return type is kind of useless, is it?

P

Peter

Posting this again, since google-groups may have swallowed it
earlier...

What is the use of the auto keyword for function return types,
if I anway have to declare the type by hand after the function header?

In this case I've could also have written the return type up front
using the template types of the variables instead of variable names.

Currently i'm writing all the code to be included in the function
using macros,
with the macro names matching the variable names.
I do this since the code is needed twice
-- once for the return type determination and once for the actual
function body
(this is some simple example demonstrating my approach.
Imagine much bigger functions):

Can somebody come up with a simpler approach?


template<typename T0, typename T1>
auto getSomething(const T0 &_r0, const T1&_r1)
#define _a (_r0 + _r1)
#define _b (_a*_a)
#define _c (_b*_a)
-> decltype(_c)
{
const auto a(_a);
#undef _a
#define _a a
const auto b(_b);
#undef _b
#define _b b
const auto c(_c);
#undef _c
#undef _b
#undef _a
return c;
}
 
J

Juha Nieminen

Peter said:
Can somebody come up with a simpler approach?

template<typename T0, typename T1>
auto getSomething(const T0 &_r0, const T1&_r1)
#define _a (_r0 + _r1)
#define _b (_a*_a)
#define _c (_b*_a)
-> decltype(_c)
{
const auto a(_a);
#undef _a
#define _a a
const auto b(_b);
#undef _b
#define _b b
const auto c(_c);
#undef _c
#undef _b
#undef _a
return c;
}

Are you entering an obfuscated C++ code contest or something? I have
really hard time deciphering that.

Anyways, what I think you want is this:

template<typename T0, typename T1>
auto getSomething(const T0& r0, const T1& r1)
-> std::common_type<T0, T1>::type
{
const auto a = r0 + r1;
const auto b = a * a;
return b * a;
}
 
S

SG

What is the use of the auto keyword for function return types,
if I anway have to declare the type by hand after the function header?

One advantage is "scope". If you declare the return type at the end
the compiler already knows the parameters including whether the
function is a member function of not.

One of the first examples to demonstrate the benifit was

template<class T, class U>
decltype(a+b) // FAILS, a and b are not yet known
add(T a, U b);

template<class T, class U>
decltype(declval<T>()+declval<U>()) // works, but is tedious
add (T a, U b);

template<class T, class U>
auto add(T a, U b)
-> decltype(a+b); // fine, compiler knows what a and b are

But it also works with member typedefs, for example:

class myuserdefinedtype
{
public:
typedef int something;
auto bar() -> something;
};

auto myuserdefinedtype::bar() -> something
{
...
}

instead of

myuserdefinedtype::something myuserdefinedtype::bar()
{
...
}

Unfortunately, the compiler allows omitting the return type entirely
only for lambdas with a single return statement. I whish I could do
the same for named functions. But it is possible to reduce redundancy
using a macro:

#define AUTO_RETURN(...) \
-> typename std::decay<decltype(__VA_ARGS__)>::type \
{ return __VA_ARGS__; }

template<class T, class U, class V>
auto baz( T a, U b, V c )
AUTO_RETURN( a*(b+c) )

For more complicated functions (more than just a single return
statement) I think it's okay to let the user specify the return type
manually.
 
S

SG

template<typename T0, typename T1>
auto getSomething(const T0& r0, const T1& r1)
-> std::common_type<T0, T1>::type

Hmm... It would be nice to be able to omit the typename keyword in
this context (like you did). After all, what follows the arrow can
only be a type. But right now, I'm not sure about whether this is
allowed or you actually still have to insert "typename" before
std::common_type<T0, T1>::type.

Anyone knows for sure? I can imagine the Microsoft compiler being lax
about it. So, a report like "MSVC compiles it" isn't good enough for
me. ;-)

Cheers!
SG
 
P

Peter

  Anyways, what I think you want is this:

template<typename T0, typename T1>
auto getSomething(const T0& r0, const T1& r1)
-> std::common_type<T0, T1>::type
{
    const auto a = r0 + r1;
    const auto b = a * a;
    return b * a;


I don't see the code where you tell the compiler that the return type
is decltype((_r0+_r1)*(_r0+_r1))*(_r0+_r1)).
 
P

Peter

One advantage is "scope".  If you declare the return type at the end
the compiler already knows the parameters including whether the
function is a member function of not.

One of the first examples to demonstrate the benifit was

  template<class T, class U>
  decltype(a+b)   // FAILS, a and b are not yet known
  add(T a, U b);


decltype(T()+U()) add(T a, U b)

so the auto keyword does not help at all.
 
S

SG

I don't see the code where you tell the compiler that the return
type is decltype((_r0+_r1)*(_r0+_r1))*(_r0+_r1)).

You don't have to use exactly the same expression for the return
type. I expect that in many cases one can greatly simplify the
decltype expression because it's only used for the type, not the
value.
decltype(T()+U()) add(T a, U b)

This only works in case T and and U are default-constructible. That's
why I used std::declval<T>() and std::declval<U>() in my example.
Surely 'a' and 'b' is much shorter than 'std::declval<T>()' and
'std::declval said:
so the auto keyword does not help at all.

That depends on what kind of problem you look at while considering
auto. As I said, it's mainly about scope and also about readability.

Cheers!
SG
 
P

Peter

You don't have to use exactly the same expression for the return
type.


I don't?
In my case
decltype(_r0+_r1)
maybe different than
decltype((_r0+_r1)*(_r0+_r1))
which in turn may be different than
decltype(std::sin(_r0+_r1))
So please tell me how I can avoid having to write the code twice?
 
S

SG


In general, no -- depends on the case and how "generic" you want to
be. You snipped half of my comment which was intended to further
qualify this statement of mine.

For example, I would probably write something like this

template<class T, class U>
auto dot_product(vector<T> const& a, vector<U> const& b)
-> decltype(a[0]*b[0]);

expecting that

decltype(a[0]*b[0]+a[1]*b[1]+...+a[n-1]*b[n-1])

can be shortened to

decltype(a[0]*b[0])

In my opinion, this is a perfectly reasonable thing to do.

Cheers!
SG
 
J

Juha Nieminen

Peter said:
I don't see the code where you tell the compiler that the return type
is decltype((_r0+_r1)*(_r0+_r1))*(_r0+_r1)).

std::common_type::type is the resulting type of any arithmetic
operations performed on the parameter types.
 
P

Peter

Guys -- this is ridiculous.

Thanks for telling me, that the problem I'm facing does not exist!

I was posting here to publish the unnecessary restrictions in the new C
++ standard
and I may have hoped, that somebody may come up with a better method.

I was of the opinion, that my original post contained enough
information for all of this.
 
P

Peter

Guys -- this is ridiculous.

Thanks for telling me, that the problem I'm facing does not exist!

I was posting here to publish the unnecessary restrictions in the new C
++ standard
and I may have hoped, that somebody may come up with a better method.

I mean to say:


that somebody may come up with a better method than using macros the
way I did...
 
J

Juha Nieminen

Peter said:
Guys -- this is ridiculous.

Thanks for telling me, that the problem I'm facing does not exist!

I was posting here to publish the unnecessary restrictions in the new C
++ standard
and I may have hoped, that somebody may come up with a better method.

I was of the opinion, that my original post contained enough
information for all of this.

What exactly is the problem with std::common_type?

It works with all basic types (for which arithmetic operators can be
applied to) and any other type if the types are the same (for example it
works with std::string just fine).

It might not work with different classes that can be inter-mixed with
the arithmetic operators. Haven't tested. OTOH, it's allowed for common_type
to be specialized.
 
S

SG

Peter, this is ridiculus. Your thread title includes a general
statement about the alleged uselessness of auto for functions and when
presented with counter examples you're not pleased. Sorry, I can't
give you a "Yes, you're right. auto sucks".
Thanks for telling me, that the problem I'm facing does not exist!

Maybe it doesn't. I yet have to see sample code that looks like it
makes sense in some context or another. The "simpler" approach you
asked for would look -- given my AUTO_RETURN macro -- like this:

template<class T, class U>
auto func(T x, U y)
AUTO_RETURN( (x+y)*(x+y)*(x+y) )

or better yet

template<class T>
auto pow3(T z)
AUTO_RETURN( z*z*z )

:::
auto result = pow3(x+y);
:::
I was posting here to publish the unnecessary restrictions in the new C
C++ standard and I may have hoped, that somebody may come up with a
better method.

The only thing I would consider an unnecessary is the restriction that
functions with a single return statement in their body still have to
define the return type manually because that's but lambdas already
allow. Beyond that, there might be good technical reasons why things
are the way they are w.r.t. deduction of return types (I dunno). But
it actually doesn't bother me that much and *maybe* you're just trying
to solve problems more complicated than necessary.
I mean to say:

that somebody may come up with a better method than using macros the
way I did...

I did. My macrro wasn't as ugly as your macro collection. :p
;-)
I was of the opinion, that my original post contained enough
information for all of this.

I expected you to be more appreciative of the feedback.
That's life.

Cheers!
SG
 
M

Marc

Juha said:
What exactly is the problem with std::common_type?

It only works for simple cases. Sure, for types short, unsigned int,
etc, the type of a-b will be common_type<A,B>::type. But already for
type char*, a-b has type ptrdiff_t but common_type is char*. If you
use expression template, every expression will have a different type
(OTOH, computing (a+b)*(a+b) with expression template instead of first
evaluating c=a+b then computing c*c is uncommon as it generally means
the addition is performed twice).

Since there is a better solution that is guaranteed to always yield
the right type...
 
P

Peter

Peter, this is ridiculus.  Your thread title includes a general
statement about the alleged uselessness of auto for functions and when
presented with counter examples you're not pleased.  Sorry, I can't
give you a "Yes, you're right. auto sucks".


counter example?
The class I'm dealing with has a default constructor.
So I don't have any advantage in putting the declspec after the
function header compared to putting it in front and using types
instead variable names.


Maybe it doesn't.  I yet have to see sample code that looks like it
makes sense in some context or another.  The "simpler" approach you
asked for would look -- given my AUTO_RETURN macro -- like this:

  template<class T, class U>
  auto func(T x, U y)
  AUTO_RETURN( (x+y)*(x+y)*(x+y) )


The point is to avoid multiple calculation of identical expressions.
This is so basic that I did not imagine I would have to point it out.
This is what I'm using variables for.
So fine -- your "solution" works for trivial functions without
temporary variables.
My example problem contained temporary variables -- your solution does
not apply!

or better yet

  template<class T>
  auto pow3(T z)
  AUTO_RETURN( z*z*z )

  :::
  auto result = pow3(x+y);
  :::


The only thing I would consider an unnecessary is the restriction that
functions with a single return statement in their body still have to
define the return type manually because that's but lambdas already
allow.  Beyond that, there might be good technical reasons why things

exactly


are the way they are w.r.t. deduction of return types (I dunno).  But
it actually doesn't bother me that much and *maybe* you're just trying
to solve problems more complicated than necessary.


tell this to my customers!
 
J

Juha Nieminen

Marc said:
It only works for simple cases. Sure, for types short, unsigned int,
etc, the type of a-b will be common_type<A,B>::type. But already for
type char*, a-b has type ptrdiff_t but common_type is char*.

std::common_type is intended for combining the parameters using
arithmetic operations. Exactly what kind of arithmetic operation
would you combine two char*'s with?

std::common_type does work properly if the parameters are eg. of
type std::string or std::complex. I imagine (although I haven't tested)
the only situation where it won't work properly is when you have two
different classes (or one clas and one basic type) that can be combined
with arithmetic operators. OTOH, in these situations it is possible to
overload std::common_type to make it work.
 
M

Marc

Juha said:
std::common_type is intended for combining the parameters using
arithmetic operations.

In special cases, and among other things.
Exactly what kind of arithmetic operation
would you combine two char*'s with?

Ever heard of "pointer arithmetic"?
std::common_type does work properly if the parameters are eg. of
type std::string or std::complex. I imagine (although I haven't tested)
the only situation where it won't work properly is when you have two
different classes (or one clas and one basic type) that can be combined
with arithmetic operators.

common_type works by looking at the result type of ?:, i.e. basically
looking at whether one type can be implicitly converted into the
other. It happens to be useful to determine the result type of
arithmetic operations on std::duration (and IIRC that was one of the
reasons why it was created), but note that even there scalar
multiplication doesn't directly define the result type as the
common_type of both arguments.

Now think of the multiplication of 2 matrices: do you expect each
matrix to be convertible to the result type? In general, do you always
expect +, -, * and / to return the same type?

common_type can also be useful in cases where there are no arithmetic
operations defined.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top