Trailing return type in non-template functions [duplicate] - c++

This question already has answers here:
Should the trailing return type syntax style become the default for new C++11 programs? [closed]
(4 answers)
Closed 2 years ago.
I have seen people using the following syntax to implement functions:
auto get_next() -> int
{
/// ...
}
Instead of:
int get_next()
{
/// ...
}
I understand both and I know that the trailing return type syntax is useful for template code using decltype. Personally I would avoid that syntax for other code since when reading code I prefer to read the concrete return type of a function first, not last.
Is there any advantage in using the trailing return type syntax for non-template code as shown above (except personal preference or style)?

In addition to sergeyrar's answer, those are the points I could imagine someone might like about trailing return types for non-template functions:
Specifying a return type for lambda expressions and functions is identical w.r.t. the syntax.
When you read a function signature left-to-right, the function parameters come first. This might make more sense, as any function will need the parameters to produce any outcome in the form of a return value.
You can use function parameter types when specifying the return type (the inverse doesn't work):
auto f(int x) -> decltype(x)
{
return x + 42;
}
That doesn't make sense in the example above, but think about long iterator type names.

One potential benefit is that it makes all of your declared function names line up:
auto get_next(int x, int y) -> int;
auto divide(double x, double y) -> double;
auto printSomething() -> void;
auto generateSubstring(const std::string &s, int start, int len) -> std::string;
source: https://www.learncpp.com/cpp-tutorial/the-auto-keyword/

Well you already mentioned the major use case of the trailing return type. It's usage if the return type depends on the arguments.
If we exclude that use case the only advantage you might gain is an enhanced readability of your code, especially if your return type is a bit more complex, but you don't have a technical benefit of using the trailing return in that scenario.

Related

Implement Clip() in Eigen

I have code that clips some value to be between a range centered around 0 like below.
Eigen::VectorXd a;
Eigen::VecotrXd b;
a = a.cwiseMin(b).cwiseMax(-b); // no temporary created here?
I want to factor out the logic into a function.
One solution:
Eigen::VectorXd Clip(const Eigen::VectorXd& a, const Eigen::VectorXd& b);
a = Clip(a, b);
But I assume this is inefficient as it creates an extra temporary?
Another solution:
void Clip(Eigen::Ref<Eigen::VectorXd> a, const Eigen::VectorXd& b) {
a = a.cwiseMin(b).cwiseMax(-b);
}
But this seems inconvenient to use sometimes:
void SomeFunctionSignatureICannotChange(const Eigen::VectorXd& a, const Eigen::VectorXd& b) {
// Eigen::VectorXd a_clipped = Clip(a, b); would be cleaner.
Eigen::VectorXd a_clipped;
Clip(a_clipped, b);
}
The best solution I can think of:
template <typename DerivedV, typename DerivedB>
auto Clip(const Eigen::ArrayBase<DerivedV>& v,
const Eigen::ArrayBase<DerivedB>& bound)
-> decltype(v.min(bound).max(-bound)) {
return v.min(bound).max(-bound);
}
(I assume 'auto' in this case is fine and not the one that common pitfalls warned against?)
However, the code seems template-heavy and a bit-complicated. For example, trailing return type is discouraged by google style guide here:
Use the new trailing-return-type form only in cases where it's
required (such as lambdas) or where, by putting the type after the
function's parameter list, it allows you to write the type in a much
more readable way. The latter case should be rare; it's mostly an
issue in fairly complicated template code, which is discouraged in
most cases.
Alternatively, if I remove the trailing return type, function return type deduction will kick in. But google style guide seems to also discourage function return type deduction in public headers here
Furthermore, use it only if the function or lambda has a very narrow
scope, because functions with deduced return types don't define
abstraction boundaries: the implementation is the interface. In
particular, public functions in header files should almost never have
deduced return types.
I'm new to Eigen and C++ so not sure if I missed anything. Hope to learn from everyone's comments and suggestions. Thanks!
I confirm that a = a.cwiseMin(b).cwiseMax(-b); does not create any temporary. In your case, using auto return type is highly recommended, and I would also argue that your use case is a perfectly legit exception to the aforementioned rules:
Writing the return type explicitly would be a nightmare and error prone.
This is a very short function that is expected to be inlined. It is thus expected to be declared and defined at the same time. Therefore the second rule does not not really apply.
In c++14 you can even omit the redundancy:
template <typename DerivedV, typename DerivedB>
auto Clip(const Eigen::ArrayBase<DerivedV>& v,
const Eigen::ArrayBase<DerivedB>& bound)
{
return v.min(bound).max(-bound);
}

Example where trailing return type must be used, because the problem cannot be solved the old way

Is there any case, where we must use trailing return type, because the problem cannot be phrased in the old way?
auto fn() -> int; can be easily transformed to the old way: int fn();.
I wonder, is there an example, where this transformation is not possible. The most straighforward example, when we refer to function parameters in the return type, seems to be solvable by using declval.
Note: don't consider lambdas here, where we must use trailing return type.
In a trailing return type, you're allowed to apply decltype to this (see this question).
With the old syntax, you'd have to spell the class name manually... which you can't do if the class is unnamed!
(Or if the member function is generated with a macro, so the class name isn't known.)
struct
{
auto foo() -> decltype(this)
{
return this;
}
/*
decltype(this) foo() // error: invalid use of 'this' at top level
{
return this;
}
*/
} x;
I admit that this is a slightly unrealistic example, and you can easily work around it by naming the class, but I couldn't think of anything else.
One bizzare example I can think of, which needs some prerequisites.
Consider a function which cannot use auto return type deduction (e.g. it has multiple return values which cannot be deduced to the same type) and uses generic function from C++ concepts. Then you don't have a type to use for std::declval and auto deduction won't work:
auto foo(auto x)
// -> decltype(x) // comment this out to fix
{
if(x > 0) return x;
return -1; // requires int to be implicite castable to type of x
}
Demo

Advantage of using trailing return type in C++11 functions

What is the advantage of specifying a trailing return type in C++11, as opposed to a normal return type? Look at foo1 vs foo2 here:
int foo1() {
return 1;
}
auto foo2() -> int {
return 1;
}
int main() {
foo1();
foo2();
}
In this example, they mean the exact same thing.
However, there are a few advantages to using the trailing return type form consistently (Phil Nash calls these "East End Functions", since the return type is on the east end).
Using parameters. Obviously when using parameters to determine the return type, you must use a trailing return type.
template <typename T>
auto print(T const& t) -> decltype(std::cout << t) { return std::cout << t; }
Name lookup. In a trailing return type, name lookup includes the class scope for member function definitions. This means you don't have to retype the class if you want to return a nested class:
Type C::foo() { ... } // error: don't know what Type is
C::Type C::foo() { ... } // ok
auto C::foo() -> Type { ... } // ok
Likewise, for defining member functions where the class name for some reason must be disambiguated to be in the global namespace and the return type is a class:
D ::C::foo() { ... } // error, parsed as D::C::foo() { ... }
auto ::C::foo() -> D { ... } // ok
A more reasonable ordering of information. Let's say you want to write a function to_string that takes an int and returns an string. That's a pretty sensible way of phrasing that. Function name, parameters, return type. You wouldn't say you want to write a string-returning function named to_string that takes an int. That's an awkward order of information. The name is the most important, followed by the parameters, followed by the return type. The trailing-return-type form allows you to order these pieces of information better.
There are cases where trailing-return-type is mandatory, there are cases where it is helpful, and there are cases where it does the same thing. There are not cases where it is worse for reasons other than simply character count.
Plus, mathemtically we're used to thinking of functions as A -> B and not so much B(A), and so auto(*)(A) -> B as a function pointer taking an A and returning a B is a little closer to that view than B(*)(A).
On the other hand, writing auto main() -> int looks ridiculous.
But honestly, that's mostly because of unfamiliarity. There's nothing inherently ridiculous about it. If anything, it's a bit unfortunate that the language uses auto here as a way to declare a function - not because it's too long (i.e. some other languages use fun or even fn) - but because it's not really distinct from other uses of auto. If it were func instead, I think it would have been better (although it would not make any sense to change now).
Ultimately, this is purely opinion based. Just write code that works.

How can I get around specifying variables in decltype expressions?

Assume I have the following exemplary function:
template <typename Fn> auto call(Fn fn) -> decltype(fn()) {
return fn();
}
The important thing about this function is that its return type depends on its template parameter, which can be inferred. So ultimately, the return type depends on how the function is called.
Now, we also have a test class:
struct X {
int u;
auto test() -> decltype(call([this]() -> double {this->u = 5; return 7.4;})) {
return call([this]() -> double {this->u = 5; return 7.4;});
}
};
as you can see, X::test calls call, returning the same return value. In this case, the return type is trivially given as double, but let's assume for a bit we didn't know what call does and that the lambda has a more complicated return type.
If we try to compile this, the compiler will complain, because we're using this at the top level (not in a scope that would allow an expression):
error: lambda-expression in unevaluated context
error: invalid use of ‘this’ at top level
However, I have to use the capture of the lambda which I pass to call in order to get call's return type right. How would you suggest to get around this, while still leaving the lambda?
Note: Of course I could move the lambda to be an operator() of some helper type, which I instantiate with a copy of the this pointer, but I'd like to avoid that boilerplate.
I think the real error to be concerned about is "lambda-expression in unevaluated context". You can't use a lambda in an unevaluated context because every lambda expression has a unique type. That is, if decltype([]{}) were allowed it would deduce a different type than []{} in some other context. I.e. decltype([]{}) fn = []{}; wouldn't work.
Unless you want to just explicitly write the return type rather than have it deduced, I don't think you have any choice but to create a real type that you can use in the contexts you need, with whatever boilerplate that entails.
Although if changing test to not be a member function is acceptable then you could use the fact that lambda's can deduce their return type by omitting it if the body is only a single return statement:
template <typename Fn> auto call(Fn fn) -> decltype(fn()) {
return fn();
}
struct X {
int u;
};
int main() {
auto test = [](X *x) { return call([x]() -> double {x->u = 5; return 7.4; });};
X x;
test(&x);
}
It would be nice if the trailing return type syntax for functions had the same property. I'm not sure why it doesn't.
It seems to be a made up (construed, artificial) question, since
If you get the lambda from somewhere else, then it's named and no problem binding this.
If you're not getting the lambda from somewhere else, then you know the result type.
In short, as the problem is currently stated (as I'm writing this answer) there's no problem except one imposed by your own will.
But if you insist on that, well, just pass this as an argument instead of binding it via the lambda definition. Then for the call to call, bind the argument. But, perhaps needless to say, since that only solves a made-up problem it's a real Rube Goldberg construction, a decent into over-flowering needless complexity that doesn't solve anything real outside its own intricacies.
What was the original real problem, if any?
You shouldn't always copy-and-paste function body to decltype. The point of introducing late-specified return type was that you'll be able to somehow infer correct return type from arguments.
e.g. auto f(T x) -> decltype(g(x)) { return h(), g(x); }, not -> decltype(h(), g(x))
So, in your case, double test() is enough, because we know behavior of call and we know return type of lambda function we pass to it.
In more complex case, we should reduce code inside decltype, by using knowledge about call and other stuff.

Return type deduction [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Omit return type in C++11
In C++11 lambda can deduce their return type if the body consists of only a return statement. A proposal is the works to remove this restriction, and apparently it's already working in GCC.
Is there a reason this couldn't be extended to all auto returning functions?
Has this extension already been proposed?
Is there a reason this couldn't be extended to all auto returning functions?
Well, there's the fact that it wouldn't be possible unless the function was defined right there (not just a declaration). You'd lose the ability to forward declare such functions.
Also, functions don't return auto. The auto in front of a function definition is a purely syntactic thing to allow for trailing return types. And the only reason the return type is specified last is so that it can refer to the function's arguments (for template and decltype work, usually). The functions still do return a specific value.
In fact there is a reason.
Namely, the name of a function is in scope inside a function, but not in the trailing-return-type specification. Lambdas are exempt because they don't have names, although I think a variable being initialized from the lambda, typed by inference, is also in scope, so they already suffer this problem even with the standard syntax (workaround).
With the name of the function in scope, it's possible to construct an infinite circular type dependency. e.g.
auto fact(int n)
{
return (n > 0)? n*fact(n-1): 1;
}
In this case typing is consistent for several choices of return type... int, long long, float, and double, as well as std::complex<double>, etc.
No problem with trailing-return-type, the code is simply illegal:
auto fact(int n) -> decltype((n > 0)? n*fact(n-1): 1) /* unknown identifier fact */
In another example, it's inconsistent for any choice of return type:
auto f(int a)
{
char r[sizeof(f(a))+1];
return r;
}
What does your new-and-improved g++ do with this?
auto fact = [&](int n){ return (n > 0)? n*fact(n-1): 1; };