Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trailing return type in C++14

Tags:

c++

c++14

auto

With the introduction of auto return type in C++14, is there any real situation that trailing return type is required or it's completely obsolete in C++14 and 17?

like image 417
Vahid Noormofidi Avatar asked Oct 10 '16 03:10

Vahid Noormofidi


People also ask

What is trailing return type in C++?

The trailing return type feature removes a C++ limitation where the return type of a function template cannot be generalized if the return type depends on the types of the function arguments.

Can you use auto as a return type in C++?

The return type, which specifies the type of the value that the function returns, or void if no value is returned. In C++11, auto is a valid return type that instructs the compiler to infer the type from the return statement. In C++14, decltype(auto) is also allowed.

What does Decltype auto do?

Use auto and decltype to declare a function template whose return type depends on the types of its template arguments. Or, use auto and decltype to declare a function template that wraps a call to another function, and then returns the return type of the wrapped function.


2 Answers

Trailing return type gives you SFINAE support. Deduced return types never cause an error early enough to be a mere substitution failure.

This permits compilers not to have to be able to compile entire arbitrary functions bodies and then back out cleanly in order to determine if an overload applies.

like image 101
Yakk - Adam Nevraumont Avatar answered Nov 08 '22 18:11

Yakk - Adam Nevraumont


Apart from where you are required to use it (and the other answers here give great examples), you can use it for clarity, for explicitly stating what the function returns. This is very important once you realize (hopefully pretty soon) that reading a code is at least as important as writing it.

consider:

auto split(gsl::cstring_span str)
{
    ...
    ...
    auto tokens = std::vector<gsl::cstring_span>();
    ...
    ...
    for (...) {
        ...
        ...
        ...
    }
    ...
    return tokens;
}

versus:

auto split(gsl::cstring_span str) -> std::vector<gsl::cstring_span>();
{
   ... doesn't even matter
}

I shouldn't be looking in the implementation to see what the contract of the function is. I could probably guess what the return type is by looking at the first example, but these kind of assumptions are dangerous in programming. I would have to scan the implementation to be sure of what I receive when I call the function. With the second example, I have the interface cleanly stated. I don't care of the implementation, as the name is self-explanatory and so I don't have to look in the definition at all.

Further more consider this even worst example. Lets find out what this function returns:

auto split(const char* str)
{
   return split(gsl::cstring_span(str));
}

Ok, now search for that overload:

auto split(gsl::cstring_span str)
{
   return split_impl(str);
}

Okey..., now lets follow that:

auto split_impl(gsl::cstring_span str)
{
   return split_impl(str, ::isspace);
}

Are you f** kidding me.

So I hope you see the point.

I am not saying always use explicit return type, I am saying consider that knowing the return type should be quickly with a simple glance at the function. Sometimes, for one-liners, that can be discovered from the body. Sometimes, it can be safely guessed from the name (e.g. is_empty() clearly returns bool). Other times, you would need to explicitly name it. Make the developer's life who uses your code later simpler, especially since that one will eventually inevitably be you.

like image 26
bolov Avatar answered Nov 08 '22 17:11

bolov