2

我已经看到,使用尾随返回类型的原因之一是我们想从输入参数中推断出返回类型。
我知道还有其他原因,但我将重点放在这个问题上。

给定的示例之一是:

template <typename T>
auto func(const T & t) -> decltype(std::cout << t)
{
    return std::cout << t;
}

但我想不出任何具体的用例。

我的意思是,我们在编写函数时总是知道函数的返回类型,当真正需要并且无法避免从参数推导返回类型时,我找不到任何具体的例子。

我们总是可以(如果我没记错的话)通过直接指定返回类型来重写函数的原型,而不进行任何推导,这在我的意义上使它更简洁明了。

上面的例子可以改写为:

template <typename T>
std::ostream& func(const T & t)
{
    return std::cout << t;
}

在我看来,这比尾随返回类型版本更简洁且更具可读性。

我错过了什么?

4

2 回答 2

7

我的意思是,我们在编写函数时总是知道函数的返回类型

我们要不要?所以如果你写这个函数模板:

template<typename A, typename B>
/* ret */ foo(A a, B b) {
  return a + b;
}

你可以肯定地说ret是什么?如果给定两个整数,那么它是一个整数,当然。但是如果提供一个整数和一个长整数,由于促销,它应该是长的。如果一个参数是双精度数,那么结果应该是双精度数。

如果这是某些类类型的两个对象呢?现在我们调用了一个重载operator+的,绝对不能猜测它会返回什么。

我希望你现在确信,通过说我们接受任何两种类型,我们不能总是确定涉及这些类型的表达式的类型是什么。

因此,在语言中添加了一种机制来告知。诚然,这个例子过于简单,很可能被返回类型所取代auto,但一般原则仍然存在。在编写泛型代码时,我们经常会处理未知类型。几乎不知道涉及它们的表达式的类型应该是什么,或者即使这样的表达式在函数被实例化之前是否有效。decltype告诉我们。

于 2019-10-18T12:34:01.860 回答
1

上面的例子可以改写为:

不,它不能。您的结论基于以下假设:

std::ostream& operator<<(std::ostream&,const T&);

但情况不一定如此。它可能是一个

any_other_type operator<<(std::ostream&,const T&);

然后您的方法版本将无法编译,而具有推导返回类型的版本就可以了。

对于一个具体的用例,考虑某种让您编写代码的 io 操作,例如

std::cout << all_caps_modifier << "some text" << back_to_normal_modifier << " more text";

那将打印:

SOME TEXT more text

我承认这是一个相当人为的例子,但是使用一些代理来封装std::cout并使字符串以大写字母打印是一种可能的实现方式(std::cout << all_caps_modifier将返回与 不同的类型std::ostream&)。

于 2019-10-18T12:33:13.037 回答