C++中的默认参数规则
C++中的默认参数规则
C++的默认参数规则其实是一个非常容易掉坑的规则,尤其是当一个函数拥有多个声明的时候,每个声明的默认参数可以各不相同,在调用时又可能与每个声明都不同;这篇博客稍微列举一下C++中的默认参数规则。
前置
在开始之前,我们先来复习一下,函数可以有多个声明,定义 是有函数体的声明。默认参数则是函数声明中使用特殊语法(decl-specifier-seq declarator = initializer
)为某个参数提供的默认值。这种语法就像在参数列表中写参数对象的拷贝初始化一样。
void foo(int a, int b = 0);
void func(int a = 1, int b = 2, int c = 3);
默认参数语法存在的目的,是为用户在函数调用时,可以不提供尾随参数。
func();
func(1);
func(1,2);
非常容易理解,由于C++目前还没有像python那样指定参数的语法,因而需要提供默认值的参数必须放到参数列表的后面。实质上,这相当于编译器替用户向函数中传递参数。
那么,这里就有这么几个问题,什么地方可以有默认参数?哪些东西可以作为默认参数?当函数有多个声明时,默认参数如何工作?
什么地方可以有默认参数?
到我开始写这篇文章为止,C++允许普通的函数声明(包括类成员函数)、lambda表达式中使用默认参数,而不允许函数指针、函数引用以及typedef声明中出现默认参数。具体可见这里。所以,在需要管理各种包装计算函数的对象的场景中(algorithm factory),由于大部分手法使用函数指针进行,保存默认参数需要使用额外的空间并在运行时完成。
这里可以稍微留个问题,std::function支持默认参数吗?如果不,为什么?
什么可以作为默认参数?
从文法上说,所有能做initializer的东西都能作为默认参数,但问题在于,有些东西出现在initializer中是不被允许的,这个范围还挺广。
- 局部变量不能在默认参数中
- this指针不能在默认参数中
- 其他的参数不能在默认参数中
- 非静态成员不能在默认参数中
- 有捕获内容的lambda表达式不能在默认参数中
int main(int argc = 0,char** argc = {0}); // ok
int foo(int a1, int a2, int a3 = a1 + a2); // bad
int func(int a1, int a2, int a3 = 1 + 2); // ok
void helper()
{
int n = 1;
int func(int a1 = n);//bad
}
//C is a class with copy constructor
C::foo(C p = *this); // bad
class C
{
static int s;
int a;
//void func(int n = a);//bad
void func(int n = s);//OK
}
//after C++11
int globalV;
void defaultFuncs(int a1 = ([]()->int {return 0; })(), int a2 = 1, int a3 = 3 + 2);//ok
//void defaultFuncs(int a1 = ([]()->int {return globalV; })(), int a2 = 1, int a3 = 2);//bad
并且,由于默认参数实质上就是编译器替用户填参数,而函数调用时会发生argument到parameter的拷贝初始化,因而这个initializer必须要能够满足到相应parameter的拷贝初始化。
除此之外,其它的东西都可以做默认参数。
多个声明与默认参数
如果每个函数都只有一个声明兼定义,那么事情会简单很多,但是,在C++中,一个函数可以有多个声明,但只能有一个定义,这和名称查找规则共同协作,构成了C++的分离编译特性。随之而来的,默认参数在多个声明之间有很有趣的工作特性。
多个声明之间的默认参数组合
第一个特性,就是不同声明之间的默认参数是能组合的。
void multi(int a1, int a2, int a3)
{
std::cout << a1 << a2 << a3 << '\n';
}
void multi(int a1,int a2, int a3 = 3);
void multi(int a1, int a2 = 2, int a3);
void multi(int a1 = 1, int a2, int a3);
int main()
{
multi();//ok ,call multi(1,2,3);
}
这是一个很隐蔽的特性,隐蔽到笔者的VS2017intelliScene会对它报错,然而编译还是能通过。事实上,标准中有这么一句规定:
所有的有默认参数的形参后面,所有的形参都必须在这个声明或者在先前的声明提供默认参数,或者是参数包。
在函数调用点,实际上的默认参数是函数所有可见声明的默认参数的联合。但是相应的,在这个可见集合中,不能有对于同一个形参重复的默认参数声明,即使是同一个值也不行。
int foo(int,int);
int foo(int a1, int a2 = 0);
int foo(int a1, int a2 = 0);//bad
内部作用域
简单而言,内部作用域中可以重新声明一个函数,并且可以忽视外部声明的默认参数。在这个内部作用域中的函数调用,其默认参数集合也是这个这个作用域中所有声明的默认参数联合。当然,这仅仅是可见声明这个概念,也就是名称查找的小游戏而已。
同理,如果你外部作用域与内部作用域均有默认参数定义,那么using会同时把默认参数导入进来。
其它的小规则
默认参数还有一些其它的小规则。
在类外定义的函数,可以将其默认参数与声明组合,但不能将成员函数变成构造函数。
虚函数的默认参数由静态类型决定。
除了调用运算符以外,其它的运算符都不能有默认参数。
对于在不同翻译单元内定义的内联函数,那么在每个翻译单元末尾,默认参数集都需要相同。
友元函数声明如果有默认参数,那么这个声明必须是定义并且在这个翻译单元中不会再有别的声明
C++中的默认参数规则的更多相关文章
- Python中的默认参数(转)
add by zhj: Python设计者为何将默认参数设计成这样呢?参见Python函数参数默认值的陷阱和原理深究 原文:https://github.com/acmerfight/insight_ ...
- python中的默认参数
https://eastlakeside.gitbooks.io/interpy-zh/content/Mutation/ 看下面的代码 def add_to(num, target=[]): tar ...
- python函数参数中带有默认参数list的坑
在python中函数参数中如果带有默认参数list遇到问题 先看一段代码 def f(x,l=[]): for i in range(x): l.append(i*i) print(l) print( ...
- 【c++】类中带默认参数的函数
反思两个问题 1. 带默认参数的函数,为何声明.定义不能同时有参数? 2. 带默认参数的函数, 为何带默认参数的参数靠后站? 上程序 #include <iostream> #includ ...
- 【转】深入理解C++的动态绑定和静态绑定 & 不要重定义虚函数中的默认参数
为了支持c++的多态性,才用了动态绑定和静态绑定.理解他们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误.需要理解四个名词:1.对象的静态类型:对象在声明时采用的类型.是在编译期确定的.2 ...
- C++函数默认参数(转)
在代码中使用到了函数的默认参数,在函数的定义和实现中都填写的默认参数,结果出现了错误: 代码: #ifndef FIRSTPAGE_H #define FIRSTPAGE_H #include < ...
- JS中给函数参数添加默认值
最近在Codewars上面看到一道很好的题目,要求用JS写一个函数defaultArguments,用来给指定的函数的某些参数添加默认值.举例来说就是: // foo函数有一个参数,名为x var f ...
- JS中给函数参数添加默认值(多看课程)
JS中给函数参数添加默认值(多看课程) 一.总结 一句话总结:咋函数里面是可以很方便的获取调用函数的参数的,做个判断就好,应该有简便方法,看课程. 二.JS中给函数参数添加默认值 最近在Codewar ...
- PHP函数中默认参数的的写法
函数可以定义 C++ 风格的标量参数默认值,如下所示: Example #3 在函数中使用默认参数 <?php function makecoffee($type = "cappucc ...
随机推荐
- JDBC执行存储过程的四种情况 (转)
本文主要是总结 如何实现 JDBC调用Oracle的存储过程,从以下情况分别介绍: [1].只有输入IN参数,没有输出OUT参数 [2].既有输入IN参数,也有输出OUT参数,输出是简单值(非列表) ...
- HDU1285_确定比赛名次
HDU1285_确定比赛名次 题目大意 有 n 个队伍, 只知道 m 条关于两支队伍之间胜负的关系. 求 排名. 排名不唯一, 此时输出编号较小的队伍的排名. 输入数据保证有一个符合要求的排名. 思路 ...
- Oracle,Mysql,SQlserver生成实体映射之SqlSugarT4
官网:http://www.codeisbug.com 代码已上传GitHub:https://github.com/SeaLee02/sealee 本篇主要讲使用SqlSugar包进行Model生成 ...
- 使用transfor让图片旋转
材料:Transform,onmouseout,onmouseover css: html: js:
- 优雅的QSignleton (四) 通过属性器实现MonoSingleton
大家都出去过周六了,而我却在家写代码T.T... 接下来介绍通过属性器实现MonoSingleton. 代码如下: MonoSingletonProperty.cs namespace QFr ...
- 小a的强迫症(组合数学)
问题描述: 小a是一名强迫症患者,现在他要给一群带颜色的珠子排成一列,现在有N种颜色,其中第i种颜色的柱子有num(i)个.要求排列中第i种颜色珠子的最后一个珠子,一定要排在第i+1种颜色的最后一个珠 ...
- 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--A-跳台阶
链接:https://www.nowcoder.com/acm/contest/90/A 来源:牛客网 1.题目描述 小明在坐景驰科技研发的无人车到达了目的地. 景驰科技(JingChi.ai)是一家 ...
- C++的句柄类
上一篇文件介绍了关于C++代理类的使用场景和实现方法,但是代理类存在一定的缺陷,就是每个代理类会创建一个新的对象,无法避免一些不必要的内存拷贝,本篇文章引入句柄类,在保持代理类多态性的同时,还可以避免 ...
- poj 3694 Network : o(n) tarjan + O(n) lca + O(m) 维护 总复杂度 O(m*q)
/** problem: http://poj.org/problem?id=3694 问每加一条边后剩下多少桥 因为是无向图,所以使用tarjan缩点后会成一棵树并维护pre数组 在树上连一条边(a ...
- jdbc学习笔记03
作业: 1. 学生表(id,age,name) 2. 插入学生 3. 修改学生 4. 删除学生 5. 查询学生 JavaBean 俗称简单的Java对象 javaBean满足以下三点 1.私有属性 2 ...