C++ 尾随返回类型 (2022)
C++ Trailing Return Types (2022)

原始链接: https://danielsieger.com/blog/2022/01/28/cpp-trailing-return-types.html

## C++ 尾随返回类型:总结 尾随返回类型是 C++11 中引入的一种声明函数返回类型的替代方法,它将返回类型放置在参数列表*之后*,使用 `auto` 和 `-> return_type`。这种语法最初是为了解决模板函数的问题,在模板函数中,返回类型取决于模板参数——这是传统从左到右的解析方式无法表达的场景。 **优点** 包括一致性(尤其是与*需要*尾随返回类型的 lambda 表达式),通过对齐函数名称提高可读性,以及在返回复杂类型时更简洁。它也模仿了数学函数记号。 **缺点** 主要在于 C++ 社区对它的熟悉度不高,可能导致混淆。它也可能导致简单函数的输入量增加,并与 `override` 说明符引入了复杂性。 虽然 C++14 的自动返回类型推导简化了许多情况,但显式返回类型仍然对清晰度和防止意外行为很有价值。最终,作者认为,虽然尾随返回类型具有优势,但其有限的优势和潜在的不一致性使其在大多数项目中作为通用用途的选择值得怀疑。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 C++ 尾随返回类型 (2022) (danielsieger.com) 5 分,由 susam 发表于 3 小时前 | 隐藏 | 过去 | 收藏 | 讨论 考虑申请YC 2025秋季批次!申请截止日期为 8 月 4 日 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请YC | 联系方式 搜索:
相关文章

原文

The other day, I was modernizing the PMP library code using clang-tidy. One of the suggestions was to use trailing function return types. I didn’t look too close at this C++11 feature before. Read on for a brief introduction and a summary of pros and cons.

What’s This?

Trailing return types are an alternative syntax introduced in C++11 to declare the return type of a function. In the old form you specify the return type before the name of the function:

With the new syntax you write auto before the function name and specify the return type after the function name and parameters:

auto max(int a, int b) -> int;

This looks strange to a C++ developer at first. However, other languages like Swift, Rust, or Haskell use a similar notation. Thinking of auto as a func keyword might help.

Background

The main motivation for this alternative syntax is to specify the return type of a function template when the return type depends on the template arguments. Consider the following function:

template<typename A, typename B>
??? multiply(A a, B b) { return a*b; }

In this case, the general return type of multiply() is decltype(a*b). However, you can’t write this using the standard function syntax. C++ parses from left to right. Therefore, the function parameters a and b are not yet in scope when specifying the return type before the function name.

There is a workaround using declval(), but this gets confusing:

template<typename A, typename B>
decltype(std::declval<A&>() * std::declval<B&>())
multiply(A a, B b) { return a*b; }

The alternative syntax solves this problem. Now you can directly write the correct return type:

template<typename A, typename B>
auto multiply(A a, B b) -> decltype(a*b) { return a*b; }

Pros

With two different ways to specify the return type the question comes up which one to use. Let’s have a look at the pros first.

Consistency

I think consistency is a strong argument for the new syntax. The example above shows that the new syntax is helpful in certain cases. Lambda functions use trailing return types as well:

auto square = [] (int n) -> int { return n*n; }

The new syntax is aligned with the general trend of modern C++ to move towards a left to right syntax:

auto message = std::string{"Hello"};
using table = std::map<std::string, int>;
auto square = [] (int n) -> int { return n*n; }

Herb Sutter goes into more detail in his Almost Always Auto Style article.

Understanding

When reading a function declaration the main thing I’m interested in is understanding what it does. Assuming that functions are properly named by their developers, the most important information is in the function name and not the return type. Therefore, it makes sense to put the name first.

A trailing return type is also closer to mathematical notation and the way we speak about functions in math. Let $f\;\colon\;\mathbb{R} \to \mathbb{R}$ be a function…

Readability

Using trailing return types neatly aligns the function names:

auto is_empty() -> bool;
auto number_of_vertices()() -> int;
auto vertices_begin() -> VertexIterator;

The notation can be shorter than the standard return type, for example when returning a type defined inside a class such as VertexIterator above. The standard syntax would require the class scope to be specified explicitly:

SurfaceMesh::VertexIterator SurfaceMesh::vertices_begin() { ... }

The new syntax is slightly more concise:

auto SurfcaceMesh::vertices_begin() -> VertexIterator { ... }

Cons

Two main drawbacks come to mind.

Lack of Familiarity

Obviously, most existing code does not use the new syntax. Therefore, not all developers are familiar with it. There’s also a pitfall with override, which has to go after the return type since override is not part of the function type:

virtual auto foo() -> int override;

I’ve seen the new syntax being used in some open source libraries. However, they are clearly in the minority. Using the new syntax therefore would make your library inconsistent with large parts of the C++ ecosystem.

More Typing

Using the new syntax can require more typing, notably for trivial functions. I don’t think that many folks would prefer writing

instead of

Excluding such cases would kill the consistency argument from above.

Conclusion

I’m not sure if using trailing return types everywhere is a good idea. The consistency argument and potential improvements in readability and understanding almost got me sold. They’re only nice to have though, and not much more.

The lack of familiarity is a major reason not to use the new syntax. Furthermore, C++14 introduced the possibility to automatically deduce the return type. With that, the multiply() example from above gets even simpler:

template<typename A, typename B>
auto multiply(A a, B b) { return a*b; }

This reduces the number of cases where the new return syntax is required, thereby making it a rather odd choice for normal function declarations.

Keep in mind though that auto return type deduction is not always what you want. It’s certainly fine for short functions like the example above. In general, however, I prefer explicit return types so that users know what they get without reading and understanding the full implementation or relying on an IDE.

Further Reading

Join the discussion on Reddit. Thanks for all the comments. Special thanks to u/jwakely for spotting an inaccuracy with std::declval<> parameter types.

联系我们 contact @ memedata.com