我什么时候应该使用 C++14 自动返回类型推导?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15737223/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When should I use C++14 automatic return type deduction?
提问by chris
With GCC 4.8.0 released, we have a compiler that supports automatic return type deduction, part of C++14. With -std=c++1y
, I can do this:
随着 GCC 4.8.0 的发布,我们有了一个支持自动返回类型推导的编译器,这是 C++14 的一部分。使用-std=c++1y
,我可以这样做:
auto foo() { //deduced to be int
return 5;
}
My question is: When should I use this feature? When is it necessary and when does it make code cleaner?
我的问题是:我应该什么时候使用这个功能?什么时候需要,什么时候让代码更干净?
Scenario 1
场景一
The first scenario I can think of is whenever possible. Every function that can be written this way should be. The problem with this is that it might not always make the code more readable.
我能想到的第一个场景是只要有可能。每个可以用这种方式编写的函数都应该是。这样做的问题是它可能并不总是使代码更具可读性。
Scenario 2
场景二
The next scenario is to avoid more complex return types. As a very light example:
下一个场景是避免更复杂的返回类型。作为一个非常简单的例子:
template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
return t + u;
}
I don't believe that would ever really be a problem, though I guess having the return type explicitly depend on the parameters could be clearer in some cases.
我不相信这真的会成为一个问题,尽管我猜在某些情况下让返回类型明确依赖于参数可能会更清楚。
Scenario 3
场景3
Next, to prevent redundancy:
接下来,为了防止冗余:
auto foo() {
std::vector<std::map<std::pair<int, double>, int>> ret;
//fill ret in with stuff
return ret;
}
In C++11, we can sometimes just return {5, 6, 7};
in place of a vector, but that doesn't always work out and we need to specify the type in both the function header and the function body. This is purely redundant, and automatic return type deduction saves us from that redundancy.
在 C++11 中,我们有时可以只return {5, 6, 7};
代替向量,但这并不总是可行,我们需要在函数头和函数体中指定类型。这纯粹是多余的,自动返回类型推导使我们免于这种冗余。
Scenario 4
场景 4
Finally, it can be used in place of very simple functions:
最后,它可以用来代替非常简单的函数:
auto position() {
return pos_;
}
auto area() {
return length_ * width_;
}
Sometimes, though, we might look at the function, wanting to know the exact type, and if it isn't provided there, we have to go to another point in the code, like where pos_
is declared.
但是,有时我们可能会查看函数,想知道确切的类型,如果那里没有提供,我们必须转到代码中的另一点,例如pos_
声明的位置。
Conclusion
结论
With those scenarios laid out, which of them actually prove to be a situation where this feature is useful in making the code cleaner? What about scenarios I have neglected to mention here? What precautions should I take before using this feature so that it doesn't bite me later? Is there anything new this feature brings to the table that isn't possible without it?
列出这些场景后,哪些场景实际上被证明是此功能有助于使代码更清晰的情况?我在这里忽略提到的场景呢?在使用此功能之前我应该采取哪些预防措施,以免它以后咬我?这个功能有什么新的东西没有它是不可能的吗?
Note that the multiple questions are meant to be an aid in finding perspectives from which to answer this.
请注意,多个问题旨在帮助您找到回答这个问题的观点。
采纳答案by Steve Jessop
C++11 raises similar questions: when to use return type deduction in lambdas, and when to use auto
variables.
C++11 提出了类似的问题:何时在 lambda 表达式中使用返回类型推导,以及何时使用auto
变量。
The traditional answer to the question in C and C++03 has been "across statement boundaries we make types explicit, within expressions they are usually implicit but we can make them explicit with casts". C++11 and C++1y introduce type deduction tools so that you can leave out the type in new places.
C 和 C++03 中问题的传统答案是“跨越语句边界,我们使类型显式,在表达式中,它们通常是隐式的,但我们可以使用强制转换使它们显式”。C++11 和 C++1y 引入了类型推导工具,以便您可以在新的地方省略类型。
Sorry, but you're not going to solve this up front by making general rules. You need to look at particular code, and decide for yourself whether or not it aids readability to specify types all over the place: is it better for your code to say, "the type of this thing is X", or is it better for your code to say, "the type of this thing is irrelevant to understanding this part of the code: the compiler needs to know and we could probably work it out but we don't need to say it here"?
抱歉,您不会通过制定一般规则来预先解决此问题。您需要查看特定的代码,并自己决定在所有地方指定类型是否有助于可读性:让您的代码说“这个东西的类型是 X”更好,还是更好地你的代码说,“这个东西的类型与理解这部分代码无关:编译器需要知道,我们可能可以解决它,但我们不需要在这里说出来”?
Since "readability" is not objectively defined[*], and furthermore it varies by reader, you have a responsibility as the author/editor of a piece of code that cannot be wholly satisfied by a style guide. Even to the extent that a style guide does specify norms, different people will prefer different norms and will tend to find anything unfamiliar to be "less readable". So the readability of a particular proposed style rule can often only be judged in the context of the other style rules in place.
由于“可读性”不是客观定义的[*],而且它因读者而异,因此您作为一段代码的作者/编辑者有责任不能完全满足于风格指南。即使风格指南确实指定了规范,不同的人也会喜欢不同的规范,并且会倾向于发现任何不熟悉的东西“可读性差”。因此,特定提议的样式规则的可读性通常只能在其他样式规则的上下文中进行判断。
All of your scenarios (even the first) will find use for somebody's coding style. Personally I find the second to be the most compelling use case, but even so I anticipate that it will depend on your documentation tools. It's not very helpful to see documented that the return type of a function template is auto
, whereas seeing it documented as decltype(t+u)
creates a published interface you can (hopefully) rely on.
您的所有场景(即使是第一个)都可以用于某人的编码风格。我个人认为第二个是最引人注目的用例,但即便如此,我预计它将取决于您的文档工具。看到记录函数模板的返回类型是 并不是很有帮助auto
,而看到它记录为decltype(t+u)
创建您可以(希望)依赖的已发布接口。
[*] Occasionally someone tries to make some objective measurements. To the small extent that anyone ever comes up with any statistically significant and generally-applicable results, they are completely ignored by working programmers, in favour of the author's instincts of what is "readable".
[*] 偶尔有人会尝试进行一些客观的测量。在很小的程度上,任何人都想出任何具有统计意义和普遍适用的结果,他们完全被工作程序员忽略,而赞成作者对什么是“可读”的直觉。
回答by Morwenn
Generally speaking, the function return type is of great help to document a function. The user will know what is expected. However, there is one case where I think it could be nice to drop that return type to avoid redundancy. Here is an example:
一般来说,函数返回类型对记录函数有很大帮助。用户将知道什么是预期的。但是,在一种情况下,我认为删除该返回类型以避免冗余可能会很好。下面是一个例子:
template<typename F, typename Tuple, int... I>
auto
apply_(F&& f, Tuple&& args, int_seq<I...>) ->
decltype(std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...))
{
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
template<typename F, typename Tuple,
typename Indices = make_int_seq<std::tuple_size<Tuple>::value>>
auto
apply(F&& f, Tuple&& args) ->
decltype(apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices()))
{
return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices());
}
This example is taken from the official committee paper N3493. The purpose of the function apply
is to forward the elements of a std::tuple
to a function and return the result. The int_seq
and make_int_seq
are only part of the implementation, and will probably only confuse any user trying to understand what it does.
此示例取自官方委员会文件N3493。该函数的目的apply
是将 a 的元素转发std::tuple
给一个函数并返回结果。在int_seq
和make_int_seq
仅仅是实现的一部分,并可能只会混淆试图了解它的任何用户。
As you can see, the return type is nothing more than a decltype
of the returned expression. Moreover, apply_
not being meant to be seen by the users, I am not sure of the usefulness of documenting its return type when it's more or less the same as apply
's one. I think that, in this particular case, dropping the return type makes the function more readable. Note that this very return type has actually been dropped and replaced by decltype(auto)
in the proposal to add apply
to the standard, N3915(also note that my original answer predates this paper):
如您所见,返回类型只不过decltype
是返回表达式的 a。此外,由于apply_
不打算让用户看到,我不确定在返回类型与 的大致相同时记录其返回类型是否有用apply
。我认为,在这种特殊情况下,删除返回类型会使函数更具可读性。请注意,这个返回类型实际上已被删除并替换decltype(auto)
为添加apply
到标准N3915 的提案中(另请注意,我的原始答案早于本文):
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
return forward<F>(f)(get<I>(forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices = make_index_sequence<tuple_size<decay_t<Tuple>>::value>;
return apply_impl(forward<F>(f), forward<Tuple>(t), Indices{});
}
However, most of the time, it is better to keep that return type. In the particular case that I described above, the return type is rather unreadable and a potential user won't gain anything from knowing it. A good documentation with examples will be far more useful.
但是,大多数情况下,最好保留该返回类型。在我上面描述的特定情况下,返回类型相当不可读,潜在用户不会从知道它中获得任何东西。带有示例的良好文档会更有用。
Another thing that hasn't been mentioned yet: while declype(t+u)
allows to use expression SFINAE, decltype(auto)
does not (even though there is a proposalto change this behaviour). Take for example a foobar
function that will call a type's foo
member function if it exists or call the type's bar
member function if it exists, and assume that a class always has exacty foo
or bar
but neither both at once:
另一件尚未提及的事情:虽然declype(t+u)
允许使用表达式 SFINAE,decltype(auto)
但不允许使用(即使有人提议更改此行为)。以一个foobar
函数为例,foo
如果它存在就会调用一个类型的成员函数,或者如果它存在则调用该类型的bar
成员函数,并假设一个类总是有精确的foo
或bar
两者都不是:
struct X
{
void foo() const { std::cout << "foo\n"; }
};
struct Y
{
void bar() const { std::cout << "bar\n"; }
};
template<typename C>
auto foobar(const C& c) -> decltype(c.foo())
{
return c.foo();
}
template<typename C>
auto foobar(const C& c) -> decltype(c.bar())
{
return c.bar();
}
Calling foobar
on an instance of X
will display foo
while calling foobar
on an instance of Y
will display bar
. If you use the automatic return type deduction instead (with or without decltype(auto)
), you won't get expression SFINAE and calling foobar
on an instance of either X
or Y
will trigger a compile-time error.
调用foobar
上的实例X
显示foo
,同时呼吁foobar
对的实例Y
显示bar
。如果您改为使用自动返回类型推导(有或没有decltype(auto)
),您将不会得到表达式 SFINAE 并调用foobar
任一实例,X
否则Y
将触发编译时错误。
回答by Gabe Sechan
It's never necessary. As to when you should- you're going to get a lot of different answers about that. I'd say not at all until its actually an accepted part of the standard and well supported by the majority of major compilers in the same way.
从来没有必要。至于你什么时候应该 - 你会得到很多不同的答案。我会说完全没有,直到它实际上是标准的一个被接受的部分,并且以同样的方式得到大多数主要编译器的良好支持。
Beyond that, its going to be a religious argument. I'd personally say never- putting in the actual return type makes code clearer, is far easier for maintenance (I can look at a function's signature and know what it returns vs actually having to read the code), and it removes the possibility that you think it should return one type and the compiler thinks another causing problems (as has happened with every scripting language I've ever used). I think auto was a giant mistake and it will cause orders of magnitude more pain than help. Others will say you should use it all the time, as it fits their philosophy of programming. At any rate, this is way out of scope for this site.
除此之外,这将是一场宗教争论。我个人认为永远不要输入实际的返回类型使代码更清晰,维护起来要容易得多(我可以查看函数的签名并知道它返回什么,而实际上必须阅读代码),并且它消除了以下可能性您认为它应该返回一种类型,而编译器认为另一种会导致问题(就像我使用过的每种脚本语言一样)。我认为 auto 是一个巨大的错误,它会造成比帮助更多的痛苦。其他人会说你应该一直使用它,因为它符合他们的编程理念。无论如何,这超出了本网站的范围。
回答by Ben Voigt
It's got nothing to do with the simplicity of the function (as a now-deleted duplicateof this question supposed).
Either the return type is fixed (don't use auto
), or dependent in a complex way on a template parameter (use auto
in most cases, paired with decltype
when there are multiple return points).
要么返回类型是固定的(不要使用auto
),要么以复杂的方式依赖于模板参数(auto
在大多数情况下使用,decltype
当有多个返回点时配对)。
回答by Jim Wood
Consider a real production environment: many functions and unit tests all interdependent on the return type of foo()
. Now suppose that the return type needs to change for whatever reason.
考虑一个真实的生产环境:许多函数和单元测试都依赖于foo()
. 现在假设无论出于何种原因,返回类型都需要更改。
If the return type is auto
everywhere, and callers to foo()
and related functions use auto
when getting the returned value, the changes that need to be made are minimal. If not, this could mean hours of extremely tedious and error-prone work.
如果返回类型auto
无处不在,并且调用者foo()
和相关函数auto
在获取返回值时使用,则需要进行的更改是最小的。如果没有,这可能意味着数小时的极其乏味且容易出错的工作。
As a real-world example, I was asked to change a module from using raw pointers everywhere to smart pointers. Fixing the unit tests was more painful than the actual code.
作为一个现实世界的例子,我被要求将一个模块从到处使用原始指针更改为智能指针。修复单元测试比实际代码更痛苦。
While there are other ways this could be handled, the use of auto
return types seems like a good fit.
虽然还有其他方法可以解决这个问题,但使用auto
返回类型似乎很合适。
回答by kaiser
I want to provide an example where return type auto is perfect:
我想提供一个返回类型 auto 是完美的例子:
Imagine you want to create a short alias for a long subsequent function call. With auto you don't need to take care of the original return type (maybe it will change in future) and the user can click the original function to get the real return type:
想象一下,您想为后续较长的函数调用创建一个短别名。使用 auto 不需要处理原始返回类型(可能以后会改变),用户可以点击原始函数来获取真正的返回类型:
inline auto CreateEntity() { return GetContext()->GetEntityManager()->CreateEntity(); }
PS: Depends on thisquestion.
PS:取决于这个问题。
回答by Serve Laurijssen
For scenario 3 I would turn the return type of function signature with the local variable to be returned around. It would make it clearer for client programmers hat the function returns. Like this:
对于场景 3,我将使用要返回的局部变量来转换函数签名的返回类型。对于讨厌函数返回的客户端程序员来说,它会更清楚。像这样:
Scenario 3To prevent redundancy:
场景 3防止冗余:
std::vector<std::map<std::pair<int, double>, int>> foo() {
decltype(foo()) ret;
return ret;
}
Yes, it has no auto keyword but the principal is the same to prevent redundancy and give programmers who dont have access to the source an easier time.
是的,它没有 auto 关键字,但原理是相同的,以防止冗余并为无法访问源代码的程序员提供更轻松的时间。