c++ 11 带有 decltype 的尾随返回类型无法按预期工作

c++ 11 Trailing return type with decltype does not work as expected

本文关键字:工作 返回类型 带有 decltype c++      更新时间:2023-10-16

为什么这"按预期"工作? 我的理解是这应该行不通:

template <class T, class U>
auto x(T a, U b) -> decltype(a<b ? a:b) {
return a > b ? a : b;
}

int main() {
cout << x<long, double>(1, 2.01) << endl;
cout << x<long, double>(5, 2.01) << endl;
}

我尝试了其他一些组合,例如:

template <class T, class U>
auto x(T a, U b) -> decltype(a<b ? a:a) {
return a > b ? a : b;
}

这样它就不会编译错误实际上第二个组合失败

compile time error:  Error C2440 'return': cannot convert from 'U' to 'T &' 

这是意料之中的。我的理解是,第一个函数也应该失败并出现相同的错误,同时它工作正常。

?:运算符的条件是什么并不重要。结果类型计算为第二和第三操作数的常见类型。下面是如何计算?:运算符的通用类型和值类别的一部分,有关完整详细信息,请参阅 cppreference.com:

如果第二个和第三个操作数是同一类型的左值,则结果类型将是该类型的左值。

如果类型是不相关的左值,则有一些更复杂的规则来确定通用类型,但结果将是 prvalue,而不是 lvalue。特别是如果这两种类型是算术类型,如doublelong,则应用通常的算术转换来获得公共类型。在longdouble的情况下,该通用类型将double。这与例如尝试添加两种不同的算术类型与+时所做的类型计算相同,因此称为通常的算术转换

因此,如果ab具有相同的类型,则decltype(a<b ? a:b)将是引用类型,否则它将不是引用类型。

这就是函数编译的原因。通用类型始终是这样,可以将两种输入类型转换为它。这也是为什么如果类型相等,函数具有未定义的行为,因为这样decltype会给出一个引用,因此您将返回对其中一个函数参数的引用。

decltype(a<b ? a:a)不适用于不同的类型,因为如上所述,aa的常见类型是a类型的参考。如果b具有不同的不相关类型,则a > b ? a : b的结果将是不能绑定到左值引用的 prvalue。

你可能会认为

template <class T, class U>
auto x(T a, U b) -> decltype(a < b ? a : b) {
return a > b ? a : b;
}
std::cout << x<long, double>(1, 2.01) << std::endl;
std::cout << x<long, double>(5, 2.01) << std::endl;

不会编译,因为尾随返回类型decltype(a < b ? a : b),即内部表达式a < b ? a : b与返回表达式不同a > b ? a : b但这是完全合法的,因为返回类型成为->之后的任何类型。

现在,为什么下面的代码没有成功编译?

template <class T, class U>
auto x(T a, U b) -> decltype(a < b ? a : a) {
return a > b ? a : b;
}
std::cout << x<long, double>(1, 2.01) << std::endl;
std::cout << x<long, double>(5, 2.01) << std::endl;

这是因为您需要从尾随返回类型中删除引用,如下所示:

#include <type_traits>
template <class T, class U>
auto x(T a, U b) -> typename std::remove_reference<decltype(a < b ? a : a)>::type {
return (a > b ? a : b);
}

现在它编译成功。在这里查看

第二个示例之所以没有std::remove_reference就无法编译,是因为作为三元运算符一部分的两种类型都是glvalues,这里说:

4( 如果 E2 和 E3 是相同类型和相同值的 gl值 类别,则结果具有相同的类型和值类别

所以a < b ? a : a的结果是glvalue.这就是您需要删除引用的原因。a < b ? a : b的结果是一个prvalue,因此,您无需删除引用。