C++11 通过 auto 与 decltype 来解决返回值难以推断的问题

785 阅读1分钟

「这是我参与11月更文挑战的第 9 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 20 篇文章

预备知识

decltype 其作用是用于查询表达式的数据类型,例如:

/// @note 用于测试的类型
const int &&foo();
const int bar();
int i;
struct A
{
    double x;
};

const A *a = new A();

// --------------------------------------------

decltype(foo()) x1;  ///< 推断类型为 const int&&
decltype(bar()) x2;  ///< 推断类型为 int
decltype(i) x3;      ///< 推断类型为 int
decltype(a->x) x4;   ///< 推断类型为 double
decltype((a->x)) x5; ///< 推断类型为 const double& (注意,加了个括号推断出的类型就不同了)

通过上面的例子我们可以看到 decltype 可以根据一个左值或者右值来推断出其类型。

返回值难以推断的问题

有时候在泛型编程时,有些函数的返回类型难以确定。例如,下面这个返回值我们难以确定:

template <typename U, typename T>
R Add(U u, T t)
{
    auto val = u + t;
    return val;
}

C++11 如何解决上面的问题

在 C++11 可以通过返回值后置来解决这个问题,即需要利用 autodecltype 。首先使用 auto 作为一个返回值占位符【auto 有一个特性:返回值占位。使用 auto 后,将由编译器自动进行确定】,返回值类型在稍后通过 decltype 推断出来。例如,我们可以写成下面这种形式

template <typename U, typename T>
auto Add(U u, T t) -> decltype(u + t) ///< 不要和 lambda 表达式混淆了
{
    auto val = u + t;
    return val;
}

至于为什么需要将返回值类型后置,这里简单说明一下。如果没有后置,则函数声明为 decltype(u+t) Add(U u,T t),但此时模板参数 tu 还未声明,编译无法通过。另外,如果非要使用返回值类型前置的形式,也可以将函数声明为 decltype((*(U*)0)+(*(T *)0)) Add(U u, T t),但这种形式比较晦涩难懂,因此不推荐采用。

autodecltype 在很多方面简化了我们的代码,解决了有时因为类型难以推断或者难以书写的烦恼 !