Advantages of arrow syntax in function declaration

694 views Asked by At

what are the advantages of using

template <typename L, typename R> 
auto getsum(L l, R r) -> decltype(l + r) {return l + r;}

over

 template <typename L, typename R>
 auto getsum(L l, R r) {return l +  r;}

isn't it compiled into the appropriate type during template instantiation?

4

There are 4 answers

0
max66 On BEST ANSWER

what are the advantages of using [trailing return type syntax] ?

A possible advantage: the trailing return type is SFINAE friendly.

Your getsum() function, with trailing return type (-> decltype( l + r )), is enabled only when exist an plus operator between l and r.

If you call it with a couple of arguments that doesn't support the sum (by example, a couple of std::vector's) you get a substitution failure that isn't an error (SFINAE).

So another getsum() function can be called.

The following is a full example that compile and where the "do something different" version is called from getsum(a, b)

#include <vector>

template <typename ... Ts>
auto getsum (Ts...)
 { /* do something different */ }

template <typename L, typename R> 
auto getsum (L l, R r) -> decltype( l + r )
 { return l + r; }

int main ()
 {
   std::vector<int> a, b;

   getsum(a, b);
 }

But if you remove the trailing return type

template <typename L, typename R> 
auto getsum (L l, R r) // <<--- no more tailing return type!!!
 { return l + r; }

the code doesn't compile anymore, because you don't have a substitution failure but an hard error.

13
dasfex On

Since C++14 the second just a more simple version of first one. Before the C++14 you can align function names with auto(so this is about codestyle).

6
rustyx On

An advantage obviously is shorter code.

But there are disadvantages, too: besides automatic return type deduction being available from C++14 only, there are cases when the return type cannot be deduced.

Suppose the complexity of the function is increased slightly:

template <typename L, typename R>
auto getsum(L l, R r) {
    if (l < 0)
        return 0;
    return l + r;
}

Now the following won't compile:

int main() {
    auto s = getsum(1L, 2); // error: inconsistent deduction for auto return type: 'int' and then 'long int'
}

We fix that by specifying the return type explicitly (for example, using the trailing return type syntax):

auto getsum(L l, R r) -> decltype(l + r) {
    if (l < 0)
        return 0;
    return l + r;
}

int main() {
    auto s = getsum(1L, 2);  // OK
}
0
Guillaume Racicot On

Your second expression might not return the same as the first. decltype has different deduction rules than auto.

To have the same return type you'd have to write this:

templtate <typename L, typename R>
auto getsum(L l, R r) -> decltype(auto) { return l + r; }

Now for the advantages of one over the other

The second snippet has the advantage of not repeating the expression. Repeating the expression can be quite cumbersome if you have complex expressions.

The advantages of the first version is that the compiler can perform SFINAE on the expression in the return type.

In C++20, you could just use concepts to constrain the function properly:

auto getsum(arithmetic auto l, arithmetic auto r) {
    return l + r;
}