余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

C++11开始起新型函数类型声明方式(c++ 函数声明)

xiyangw 2022-12-03 13:15 16 浏览 0 评论

概述

对于传统的C和C++编程语言中,函数的声明方式为:

<返回类型>  <函数标识符>  ( <形式参数列表> )

而在C++11标准(ISO/IEC 14882:2011)起,由于引入了现代化编程语言中常用的类型自动推导,因此为了能使该语法体系更为完备,从而让函数定义可以通过 形参类型 或 根据函数体中的返回语句直接推导出该函数的返回类型。所以引出了新型的函数声明方式:

auto  <函数标识符>  ( <形式参数列表> )  ->  <返回类型>

如果当前定义的函数可直接通过其函数体中的返回语句直接推导出其返回类型,那么后面的 “ -> <返回类型> ” 可省。

下面就来针对上述两种情况分别进行举例:

#include <cstdio>
#include <typeinfo>

// 根据形参类型来推导其返回类型
template <typename S, typename T>
static auto Test1(const S& s, const T& t) -> decltype(s + t)
{
    return s - t;
}

// 根据返回表达式类型来直接推导其返回类型。
// 注意!这里没有显式声明Test2函数的返回类型。
static auto Test2(float f)
{
    auto const value = Test1<int, unsigned>(100, 20U);
    // MSVC++下输出:value type: unsigned int, value = 80
    printf("value type: %s, value = %u\n", typeid(value).name(), value);

    return f + 1.0f;
}

对于第一个Test1函数的声明中,由于在 -> 符号之前,形参s和t对编译器都已经可见,因此可在后续直接对它们进行引用,从而可通过 decltype(s + t) 做类型推导。而对于第二个Test2函数的声明中,我们可以看到这里没有 -> 以及后续的类型说明,而直接跟函数体。因为这里编译器可直接根据该函数的返回语句直接获得该函数的返回类型。

因此,如果我们用Visual Studio 2019或更新版本的话,直接将鼠标光标移动到Test2标识符上,就会自动出现对该函数的完整类型说明,如下图所示。


特点

玩过Rust或Swift编程语言的朋友们想必对C++11所引入的新型函数声明方式感到很熟悉了吧。其实从语言表达力上而言,这种函数声明方式可更容易地与函数调用表达式予以区分,而这对于C++语言而言尤为有用。下面我们就来看些例子:

#include <cstdio>
#include <functional>

extern "C" auto CPPTest()
{
    decltype(int()) const a = int();

    const std::function<int()>& fun = [a]() {
        return a + 1;
    };

    // 输出:value = 1
    printf("value = %d\n", fun());
}

对于上述代码,笔者估计会让很多C++初学者望而生畏……那我们先来一条条分析。

我们先看第一条语句中的 decltype(int()) 里的 int() 表示啥?要解释这个问题之前,我们必须要了解——C++11中的 decltype() 里必须是一个表达式而不能是一个类型名!所以这里的 int() 其实是利用 int 类型构造一个其默认值的对象,换句话说就是构造一个值为 0 int 类型对象,使得 decltype(int()) 可直接推导出 int 类型。此外,该语句中 = 右边的 int() 表达式也是如此。所以整条语句就相当于:int const a = 0;

然后再看第二条语句,这里的 std::function<int()> 中的 int() 就表示函数类型了。因为根据 std::function 模板形参的定义可知,这里需要传递的是一个表征该函数对象的函数类型,因此这里指明的是返回类型为 int 且不带任何形参的函数类型。

所以我们在使用C++编程语言时会时常碰到类似的代码。因此笔者之前有一个良好的习惯就是对于函数声明,倘若该函数没有任何形式参数,那么直接用 void 填充。这么一来函数声明与函数调用就能很好地区分开了。我们可以用此方式改写一下上述代码中的第二条语句:

    const std::function<int(void)>& fun = [a]() {
        return a + 1;
    };

是不是感觉好多了?

但是,一些现代IDE工具,尤其是使用Clang语法检查工具的IDE,诸如CLion以及Android Studio,对于C++语言(注意,这里只针对C++,而不是C语言。C语言是完全合法且必要的。)中在形参列表里使用 void 的地方都会报出警告提示:形参列表不需要使用void,建议移除。为了迎合这些语法检测工具的口味,笔者当前也更偏好于使用新型方式来声明函数类型。


使用新型方式声明函数类型

以上提到的都是新型的函数声明方式。那对于函数类型本身而言呢,我们当然也可以使用这种声明方式。因此我们可以将上述代码的第二条语句写成以下形式:

    const std::function<auto () -> int> &fun = [a]() {
        return a + 1;
    };

各位可以看到,跟Rust、Swift非常相似吧~而这种形式显然可以与函数调用完全区分开了。

笔者下面将例举一些更高级的例子供大家参考,从而可以激发起各位的Hack及Geek精神~

#include <cstdio>
#include <functional>

// 函数Foo2的类型为:auto () -> int
static auto Foo2() { return 10; }

// 函数Foo的老旧声明表达为:
// int (*Foo(int a)) (void)
static auto Foo(int a) -> auto (*)() -> int
{
    printf("a is %d in Foo!\n", a);
    return &Foo2;
}

extern "C" auto CPPTest()
{
    // 函数类型的别名定义
    typedef auto func_type() -> int;
    using ft = auto (void) -> int;

    auto (*pFunc)(int) -> auto (*)() -> int = &Foo;

    // 输出:a is 20 in Foo!
    auto a = pFunc(20)();

    // 输出:Finally, a = 10
    printf("Finally, a = %d\n", a);
    
    struct S
    {
        auto method(double d)
        {
            return float(d - 1.0);
        }
    } s;

    auto (S::* pMethod)(double) -> float = &S::method;

    auto const f = (s.*pMethod)(2.5);
    // 输出:f = 1.500000
    printf("f = %f\n", f);
}

通过上述代码,我们可以脑补到关于函数指针类型的新型声明方法:

auto  (* <函数指针标识符>) ( <形参列表> )  ->  <返回类型>

而指向某个类类型成员方法的指针的声明方法为:

auto  (<类型名> :: * <指针标识符> )( <形参列表> ) -> <返回类型>


各位觉得是不是颇有意思?如果还有其他新奇的想法或是有任何疑问,欢迎在底下留言~

相关推荐

Vue的框架(了解)

前端MVC设计模式MVC设计模式,其实就是将前端实现某个业务的所有代码划分为三部分Model:模型,指数据模型,这个数据一般来自于服务器View:视图,指页面标签内容Controller:控制...

Vue.js实战 第五章练习一

练习要求:在原有表格基础上,新增一项是否选中该商品的功能,总价变为只计算选中商品的总价,同时提供一个全选的按钮。实现思路:按照vue数据和dom元素双向绑定的特性,定义allCheckStatus变量...

Vue基础到进阶教程之class和style绑定

关于class和style我们并不陌生,这个在学习css的时候就是家常便饭了,操作元素的class列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用v-bind处理它们,...

深入Vue 必学高阶组件 HOC「进阶篇」

作者:ssh转发连接:https://mp.weixin.qq.com/s/seKoLSIMtTd1sU4uDrgZCA前言高阶组件这个概念在React中一度非常流行,但是在Vue的社区里讨论...

周末大礼包,23道高质量中级前端面试题。金九银十,建议收藏

这套面试题考察的内容比较常见,涉及到JavaScript、ES6、CSS、Vue、简单算法,浏览器相关知识等。题目列表1.JavaScript的数据类型有哪些2.什么是同源策略3.跨域的方法...

vue3.0-摒弃Object.defineProperty,基于 Proxy 的观察者机制

写在前面:11月16日早上,Vue.js的作者尤大大在VueToronto的主题演讲中预演了Vue.js3.0的一些新特性,其中一个很重要的改变就是Vue3将使用ES6的Proxy作...

程序员都必掌握的前端教程之VUE基础教程(七)

阅读本文约需要10分钟,您可以先关注我们,避免下次无法找到。本篇文章成哥继续带大家来学习前端VUE教程,今天主要讲解VUE的表单处理等知识点。下面我们就一起来学习该块内容吧!01简介在日常开发中,我...

web前端开之网站搭建框架之vue详解

网站搭建框架之vueVue是web前端快速搭建网站的框架之一。它与jQuery有所不同,是以数据驱动web界面(以操作数据改变页面,而jQuery是以操作节点来改变页面),同时,vue还实现了数据的双...

vue3.0尝鲜-基于 Proxy 的观察者机制探索

Vue.js的作者尤大大在VueToronto的主题演讲中预演了Vue.js3.0的一些新特性,其中一个很重要的改变就是Vue3将使用ES6的Proxy作为其观察者机制,取代之前使用...

TypeScript 设计模式之观察者模式

一、模式介绍1.背景介绍在软件系统中经常碰到这类需求:当一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。这是建立一种「对象与对象之间的依赖关系」,一个对象发生改变时将「自动通知其他...

vue面试3

1.单页面应用与多页面应用的去别2.简述一下Sass、Less,且说明区别?他们是动态的样式语言,是CSS预处理器,CSS上的一种抽象层。他们是一种特殊的语法/语言而编译成CSS。变量符不一样,les...

VUE v-bind 数据绑定

动态的绑定一个或多个attribute,也可以是组件的prop。缩写::或者.(当使用.prop修饰符)期望:any(带参数)|Object(不带参数)参数:attrOrP...

vue初学习之自定义选择框实现

v-model简单介绍在使用vue的过程中会经常用到input和textarea这类表单元素,vue对于这些元素的数据绑定和我们以前经常用的jQuery有些区别。vue使用v-model实现这些标签...

Vue实现拖拽穿梭框功能四种方式

一、使用原生js实现拖拽打开视频讲解更加详细Vue实现拖拽穿梭框功能的四种方式_哔哩哔哩_bilibili<html><head><meta...

Vue3.x setup 语法糖实现props双向绑定

1.背景为了封装一下Element-Plus的分页插件,需要实现父子组件之间的传值。2.父组件<scriptsetuplang="ts">letqueryPa...

取消回复欢迎 发表评论: