与模板参数推导和auto推导一样,decltype的结果大多数情况下是正常的,但是也有少部分情况是反直觉的。

decltype介绍

给定一个name或者expression,decltype会告诉你它的类型。

我们先从正常情况开始:

const int i = 0;            // decltype(i) is const int
bool f(const Widget& w); // decltype(w) is const Widget&
// decltype(f) is bool(const Widget&) struct Point {
int x, y; // decltype(Point::x) is int
}; // decltype(Point::y) is int Widget w; // decltype(w) is Widget
if (f(w)) … // decltype(f(w)) is bool template<typename T> // simplified version of std::vector
class vector {
public:

T& operator[](std::size_t index);

}; vector<int> v; // decltype(v) is vector<int>

if (v[0] == 0) … // decltype(v[0]) is int&

很直观,没有例外情况。 注意:decltype与auto不同,不会消除const和引用。

为什么需要decltype

比如我们需要声明一个函数模板,函数的返回值类型依赖函数参数的类型。在C++11中,常见的例子是返回一个container对应索引的值:

template <typename Container, typename Index> // works, but requires refinement
auto authAndAccess(Container &c, Index i) -> decltype(c[i]) {
return c[i];
}

注意:这里的auto跟类型推导没有任何关系,它只是表明了这里使用了C++11的trailing return type.

decltype(auto)

在C++11中只允许单语句的lambda表达式被推导,在C++14中之中行为被拓展到所有lambda和所有函数,包括多语句。在C++14中,上述代码我们可以简写为:

template<typename Container, typename Index>        // C++14;  not quite correct
auto authAndAccess(Container& c, Index i) {
return c[i]; // return type deduced from c[i]
}

注意:这里的auto就跟类型推导有关系了。 在前面讲auto推导规则的文章中提到过,auto作用在函数返回值时,使用的是模板参数推导规则,这里就会出现问题:operator []我们希望它返回引用,但是使用auto使用模板参数推导规则时,引用会被忽略,所以下面的代码会报错:

template <typename Container, typename Index>
auto authAndAccess(Container &c, Index i) {
return c[i];
} std::vector<int> v{1,2,3,4,5};
authAndAccess(v,2) = 10; // error: expression is not assignable

但是使用auto -> decltype()则不会报错,因为这里auto不代表参数参数推导:

template <typename Container, typename Index>
auto authAndAccess(Container &c, Index i) -> decltype(c[i]) {
return c[i];
} std::vector<int> v{1,2,3,4,5};
authAndAccess(v,2) = 10;

所以,要想让authAndAccess在使用auto的情况下返回引用,在C++14中,我们可以使用decltype(auto):

template <typename Container, typename Index>
decltype(auto) authAndAccess(Container &c, Index i) {
return c[i];
} std::vector<int> v{1,2,3,4,5};
authAndAccess(v,2) = 10;

decltype(auto)中的auto代表返回值需要被自动推导,decltype代表使用decltype来推导返回值类型。

decltype(auto)不仅可以声明函数返回值,还可以声明变量:

Widget w;

const Widget& cw = w;       // auto type deduction : myWidget1's type is Widget

decltype(auto) myWidget2 = cw;       // decltype type deduction : myWidget2's type is const Widget&

注意(entity)

decltype的规则可以看官网:decltype specifier,概况下可以分为两大类:

  • decltype ( entity ) : 如果entity是一个不被括号包围的标识符、类访问表达式,那么decltype ( entity )与entity类型一致。
  • decltype ( expression ) : 如果expression是一个表达式,计算结果为类型T,那么:
    • 如果expression为xvalue,那么decltype的结果是T&&.
    • 如果expression为lvalue,那么decltype的结果是T&.
    • 如果expression为prvalue,那么decltype的结果是T.

注意第一点中强调了entity是一个不被括号包围的标识符。因为当一个标识符被括号包围时,它就是一个左值表达式了,对应上面第二大点的第二小点。比如说int x = 0;,x是一个标识符,所以decltype(x)的结果为int。但是(x)就是一个左值表达式,decltype((x))的结果就是int&。所以下面的用法是不同的:

decltype(auto) f1() {

int x = 0;



return x; // decltype(x) is int, so f1 returns int

}

decltype(auto) f2() {

int x = 0;



return (x); // decltype((x)) is int&, so f2 returns int&

}

官网的例子能很好的概况decltype最常见的用法:

#include <iostream>

struct A { double x; };
const A* a; decltype(a->x) y; // type of y is double (declared type)
decltype((a->x)) z = y; // type of z is const double& (lvalue expression) template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // return type depends on template parameters
// return type can be deduced since C++14
{
return t + u;
} int main()
{
int i = 33;
decltype(i) j = i * 2; std::cout << "i = " << i << ", "
<< "j = " << j << '\n'; auto f = [](int a, int b) -> int
{
return a * b;
}; decltype(f) g = f; // the type of a lambda function is unique and unnamed
i = f(2, 2);
j = g(3, 3); std::cout << "i = " << i << ", "
<< "j = " << j << '\n';
}

(完)

朋友们可以关注下我的公众号,获得最及时的更新:

c++11-17 模板核心知识(九)—— 理解decltype与decltype(auto)的更多相关文章

  1. c++11-17 模板核心知识(十一)—— 编写泛型库需要的基本技术

    Callables 函数对象 Function Objects 处理成员函数及额外的参数 std::invoke<>() 统一包装 泛型库的其他基本技术 Type Traits std:: ...

  2. c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters

    概念 举例 模板的模板参数的参数匹配 Template Template Argument Matching 解决办法一 解决办法二 概念 一个模板的参数是模板类型. 举例 在c++11-17 模板核 ...

  3. c++11-17 模板核心知识(十四)—— 解析模板之依赖型模板名称(.template/->template/::template)

    tokenization与parsing 解析模板之类型的依赖名称 Dependent Names of Templates Example One Example Two Example Three ...

  4. c++11-17 模板核心知识(十五)—— 解析模板之依赖型类型名称与typename Dependent Names of Types

    模板名称的问题及解决 typename规则 C++20 typename 上篇文章c++11-17 模板核心知识(十四)-- 解析模板之依赖型模板名称 Dependent Names of Templ ...

  5. c++11-17 模板核心知识(五)—— 理解模板参数推导规则

    Case 1 : ParamType是一个指针或者引用,但不是universal reference T& const T& T* Case 2 : ParamType是Univers ...

  6. c++11-17 模板核心知识(二)—— 类模板

    类模板声明.实现与使用 Class Instantiation 使用类模板的部分成员函数 Concept 友元 方式一 方式二 类模板的全特化 类模板的偏特化 多模板参数的偏特化 默认模板参数 Typ ...

  7. c++11-17 模板核心知识(一)—— 函数模板

    1.1 定义函数模板 1.2 使用函数模板 1.3 两阶段翻译 Two-Phase Translation 1.3.1 模板的编译和链接问题 1.4 多模板参数 1.4.1 引入额外模板参数作为返回值 ...

  8. c++11-17 模板核心知识(八)—— enable_if<>与SFINAE

    引子 使用enable_if<>禁用模板 enable_if<>实例 使用Concepts简化enable_if<> SFINAE (Substitution Fa ...

  9. c++11-17 模板核心知识(三)—— 非类型模板参数 Nontype Template Parameters

    类模板的非类型模板参数 函数模板的非类型模板参数 限制 使用auto推断非类型模板参数 模板参数不一定非得是类型,它们还可以是普通的数值.我们仍然使用前面文章的Stack的例子. 类模板的非类型模板参 ...

随机推荐

  1. Spring 最常用的 7 大类注解,哪些你还不知道?

    随着技术的更新迭代,Java5.0开始支持注解.而作为java中的领军框架spring,自从更新了2.5版本之后也开始慢慢舍弃xml配置,更多使用注解来控制spring框架. 而spring的的注解那 ...

  2. Shell脚本实现----Kubernetes单集群二进制部署

     Shell脚本实现----Kubernetes单集群二进制部署   搭建Kubernetes集群环境有以下三种方式: 1. Minikube安装方式Minikube是一个工具,可以在本地快速运行一个 ...

  3. selenium中如何保证操作元素的成功率?也就是说如何保证我点击的元素一定是可以点击的?

    1.在寻找元素时,加上显示等待或者隐式等待,这样在对元素进行操作之前保证元素被找到,进而提高成功率: 2.在对元素操作之前,比如click,如果该元素未display(非hidden),就需要先滚动到 ...

  4. Flutter源码剖析(一):源码获取与构建

    概述 本文介绍了Flutter源码的获取与构建,后面会另有文章介绍Flutter源码的版本管理.开发环境搭建等主题. 准备工作 Flutter源码分为两个部分: flutter/flutter是框架层 ...

  5. 彻底搞明白this

    this是我们在书写代码时最常用的关键词之一,即使如此,它也是JavaScript最容易被最头疼的关键词.那么this到底是什么呢? 如果你了解执行上下文,那么你就会知道,其实this是执行上下文对象 ...

  6. k8s中pod的yaml文件全面解读

    apiVersion: v1 #必选,版本号,例如v1,版本号必须可以用 kubectl api-versions 查询到 . kind: Pod #必选,Pod metadata: #必选,元数据 ...

  7. Python的Opencv库怎么装

    原文章写于时间2019.4 当时鼓捣Opencv库弄了好长时间,前前后后弄了五天,找了好多帖子不知道删除重装了多少次,现在把我试出来正确的方法给大家分享一下. 1.Pycharm 我用的是win10系 ...

  8. 删除指定路径下指定天数之前(以文件的最后修改日期为准)的文件:BAT + VBS

    代码如下: @echo off ::演示:删除指定路径下指定天数之前(以文件的最后修改日期为准)的文件. ::如果演示结果无误,把del前面的echo去掉,即可实现真正删除. ::本例调用了临时VBS ...

  9. MySQL各版本connector net msi

    从其他博主那里扒来的! 链接:https://pan.baidu.com/s/1C1fYepBFKfxU0NJS0aRyJw 提取码:awsl

  10. 求两个数的最大公约数&求N个数的最大公约数

    一.求两个数的最大公约数 如何编程计算N个数的最大公约数(Greatest common divisor)呢?第一想法那便是两两计算,但是往往最简单的想法是不怎么靠谱的.下面用递归来解决.递归有一大好 ...