4

我正在阅读有关“新” 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;
}

感觉更“自然”,因为返回类型是在通常的位置声明的,尽管是两个参数的结果。这是否与其他东西发生冲突,或者如果语法是这种不值得的方式,它是否会给编译器带来额外的工作?

4

2 回答 2

4

笔记

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

这后来在 C++14 中得到修复,编译器不需要尾随返回类型,因为它可以从返回的表达式中推断返回类型。以下在 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充当类型的惰性实例化,并且从不计算,例如,调用构造函数等。它主要用于推断类型。

于 2017-05-10T12:56:22.767 回答
2

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

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

在这里,lhsand在声明之前rhs使用。

这很难做对。

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

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

自 C++11 以来,C++ 有意识地选择关注现有 C++ 编译器中哪些是难实现的,哪些是容易实现的。那里有许多 C++98/03 特性很难实现,没有人真正做到,在某些情况下是不可能的(从 .cpp 文件发布模板,以及 std::string 的一些要求,是两个他们从我的头顶上掉下来)。

于 2017-05-10T12:13:14.180 回答