当前位置: 首页 > 知识库问答 >
问题:

尾部返回类型中decltype的原因

刘博文
2023-03-14

我在读一篇关于“新”C特性的文章时,遇到了decltype和它的用法。我理解decltype后面的原因,比如后面的返回类型

template <typename lhsT, typename rhsT>
auto add(lhsT& lhs, rhsT& rhs) -> decltype(lhs + rhs) {
    return lhs +rhs;
}

没有它,编译器将无法派生模板函数的返回类型。但是为什么语法是这样的呢?

为什么不使用

template <typename lhsT, typename rhsT>
decltype(lhs + rhs) add(lhsT& lhs, rhsT& rhs) {
    return lhs +rhs;
}

这会让人感觉更“自然”,因为返回类型是在正常情况下声明的,尽管这是两个参数的结果。这是因为它与其他东西冲突,还是因为如果语法是不值得的,它会给编译器带来额外的工作?

共有2个答案

屠兴旺
2023-03-14

编译器很难在引入符号之前引入符号。

template <typename lhsT, typename rhsT>
decltype(lhs + rhs) add(lhsT& lhs, rhsT& rhs) {
  return lhs +rhs;
}

在这里,lhsrhs在声明之前使用。

这很难做到正确。

template <typename lhsT, typename rhsT>
auto add(lhsT& lhs, rhsT& rhs) -> decltype(lhs + rhs) {
  return lhs +rhs;
}

在这里,它们是在声明后使用的。这很容易弄对。

C,因为C 11已经做出了一个有意识的选择,要注意在现有的C编译器中什么是困难的,什么是容易实现的。这里有许多C 98/03的特性,这些特性很难实现,以至于没有人真正实现,而且在某些情况下是不可能实现的(从.cpp文件发布模板,以及std::string的一些要求,是我脑子里想不到的两个)。

亢胤运
2023-03-14

没有它,编译器将无法派生模板函数的返回类型。

这后来在C 14中得到修复,html" target="_blank">编译器不需要尾随返回类型,因为它可以从返回的表达式中推断返回类型。以下在C 14中运行良好。

template <typename lhsT, typename rhsT>
auto add(const lhsT& lhs, const rhsT& rhs) {
    return lhs + rhs;
}

但是为什么语法是这样的呢?

当从参数推导出返回类型时,使用尾随返回类型很有用,参数总是在返回类型之后声明(不能引用尚未声明的内容)。

此外,从函数和数学表示法使用这种声明的意义上讲,尾随返回类型似乎更自然。我认为C风格声明之外的大多数类型的表示法通常使用尾随返回类型。例如。:

f:X→ Y

在上面的数学表示法中,f是函数名,X是参数,Y是返回值。

仅仅因为粉色的羊群拒绝灰色的羊,并不意味着这是不自然的。

在第二个示例中,编译器不可能根据上述原因推断返回类型,即参数尚未声明。但是,如果使用模板参数和std::declval,则可以进行推断,例如:

template <typename T, typename U>
decltype(std::declval<T>() + std::declval<U>()) add(const T& lhs, const U& rhs) {
    return lhs + rhs;
}

std::declval充当类型的惰性实例化,从不求值,例如调用构造函数等。它主要用于推断类型。

 类似资料:
  • 在试图理解与c 17兼容的代码时,我被下面的代码搞糊涂了,其中函数在尾部返回类型中使用integral_constant类型参数的值。(请随时更正我的术语等,尝试学习) 下面展示了两个简单的版本,在尾部返回中的参数上有decltype和没有decltype。 使用编译器资源管理器https://godbolt.org/z/vqmzhu 第一个(bool_from1)在 MSVC 15.8上编译ok

  • 在C 11中,如何专门化一个函数模板,该模板使用decltype声明为“复杂”的尾部返回类型?以下内容适用于GCC,但在VC2013中产生了“错误C2912:显式专业化‘int f(void)’i不是功能模板的专业化”: 我说“复杂”是因为缺乏一个技术上精确的词,因为我不确定是什么造成了差异。例如,以下decltype使用不依赖于任何函数结果类型的内置操作,可以与模板专业化配合使用: 自动f()-

  • 尾部调用优化对返回void的函数的递归调用有效吗?例如,我有一个函数,void fun() 在这里,编译器不会知道,调用fun()是最后一条语句。那么尾部调用优化是否只针对返回某些值的函数?

  • [采访问题] 编写一个函数,该函数将在一次传递中从单个链表整数的尾部(或尾部)返回第5个元素,然后提供一组针对该函数的测试用例。 这类似于问题:如何从单链表的末尾找到第n个元素?,但是我还有一个额外的要求,我们应该只遍历链接列表一次。 这是我的解决方案: 我只遍历链表一次,并使用隐式递归堆栈。另一种方法是有两个指针:快指针和慢指针,快指针是比慢指针快的k个指针。哪一个看起来更好?我认为有两个指针的

  • 我有一个任务,我们要创造一个石头,纸,剪刀的游戏。它指定我们必须创建一个抽象的“工具”类,其中有三个子类:“ToolRock”、“ToolPaper”、“ToolScissors”。抽象类应该有一个“+getFeagnet():tool”函数(用斜体写成)。 我的假设是做一个像这样的抽象函数: RockTool类被指定为具有函数“+get弱点():tool”(不是用斜体写的),我的想法是创建一个覆

  • 我试图创建一个Java方法,它接受一个对象类型和它应该转换成的数据类型。 例如,如果我应该能够返回一个值1作为Int或双根据需要。我使用类传递数据类型作为参数。 问题:如何使方法泛型以接受基于输入参数的返回类型? 下面的代码只是一个示例,它可能在语法上不正确,用于解释我的问题。