**extern "C"**
指定函数段为C类型函数,C类型函数是没有函数重载的,而C++类型函数是有的,这里涉及到mangling的问题,比如C程序调用C++的函数,在链接时会报错,这就是因为名称的问题:
int add(int a, int b){return a + b;}
在C++中,上述函数名称会被mangling成addii(也就是add(int,int))在链接过程中,C++程序就直接按照这个名称去寻找然调用,注意函数重载:简单理解就是函数名称一样,但形式参数不一样,而C中函数名称会变为add,C程序按照add去寻找,自然就找不到addii;为了解决这个问题,同时考虑到不同编译器mangling的名称不一样,于是就有了extern "C",addii —> add,从而转化为C类型函数。
// 其余代码extern "C"int add(int a,int b){return a + b;}extern "C"int add(int x){return x + 1;}
这段代码是错误的,出现了链接问题,在链接过程中,出现了两个add函数,在调用过程中于是就会报错,这也说明了C没有函数重载。
实参到形参的拷贝求值顺序不定
C++17 强制省略复制临时对象
CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.21)project(project0_0)set(CMAKE_CXX_STANDARD 11)add_executable(project0_0 main.cpp)
第一段代码:
#include <iostream>struct Str{Str() = default;Str(const Str&){std::cout << "Copy constructor is called" << std::endl;}};void fun(Str par){}int main(){Str val;fun(val);}
编译结果:
D:\codework\project0.0\cmake-build-debug\project0_0.exeCopy constructor is called进程已结束,退出代码0
第二段代码:
#include <iostream>struct Str{Str() = default;Str(const Str&){std::cout << "Copy constructor is called" << std::endl;}};void fun(Str par){}int main(){fun(Str{}); //临时变量}
编译结果:
D:\codework\project0.0\cmake-build-debug\project0_0.exe进程已结束,退出代码0
从第一段代码可以看出,在调用fun()函数时,val会拷贝复制到Str,从而调用拷贝复制构造函数;
从第二段代码可以看到作为临时变量,编译器进行了自动优化,省略了临时变量的拷贝复制,注意此时的C++标准为C++11,这并不与C++17开始强制省略复制临时对象相矛盾,编译器会对临时对象的复制自动优化的。
我们可以更改编译器选项,CMakeLists.txt更改如下:
cmake_minimum_required(VERSION 3.21)project(project0_0)set(CMAKE_CXX_STANDARD 11)add_executable(project0_0 main.cpp)add_definitions(-fno-elide-constructors) // 强制避免编译器优化
而第二段代码的编译结果如下,符合预期:
D:\codework\project0.0\cmake-build-debug\project0_0.exeCopy constructor is called进程已结束,退出代码0
此外在此基础上把C++标准更改为C++17,第一段代码仍有输出,第二段代码就不会有任何输出,与C++17强制省略复制临时对象这个特性符合
函数传参过程中的类型退化
#include <iostream>void fun1(int a[]){}int main(){int a[3];fun1(a);}// Insight#include <iostream>void fun1(int * a){}int main(){int a[3];fun1(a);}
变长参数 :initializer_list | 可变长度模板参数 | 使用省略号表示形式参数
#include <iostream>#include <initializer_list>void fun(std::initializer_list<int> par){}//void fun(const std::initializer_list<int>& par){// }int main(){fun({1,2,3,4,5}); // 变长参数}
warning:
#include <iostream>#include <initializer_list>std::initializer_list<int> fun(){return {1,2,3,4,5};}int main(){fun();}
这段代码是“非常危险的”,注意此时fun()函数调用结束后,返回值的生命周期已经结束,如果此时再对其获取成员或者使用相关方法,这就是未定义的行为
函数可以定义缺省实参
– 如果某个形参具有缺省实参,那么它右侧的形参都必须具有缺省实参
#include <iostream>#include <initializer_list>void fun(int x = 0,int y = 2){ // void fun(int x = 0,int y)就是错误的std::cout << x << std::endl;std::cout << y << std::endl;}int main(){fun(1,2);fun(); // 输出结果就是// 1// 2}
– 在一个翻译单元中,每个形参的缺省实参只能定义一次
– 具有缺省实参的函数调用时,传入的实参会按照从左到右的顺序匹配形参
– 缺省实参为对象时,实参的缺省值会随对象值的变化而变化
#include <iostream>#include <initializer_list>int x = 3;void fun(int y = x){std::cout << y << std::endl;}int main(){//x = 4; // 仅有11行代码 --> 4//int x = 4; // 仅有12行代码 --> 3fun(); // fun(x);}
输出结果:
3
小心返回自动对象的引用或指针
#include <iostream>int& fun1(){int x = 3;return x;}int* fun2(){int x = 3;return &x;}int main(){int& ref = fun1();int* ptr = fun2();}
自动对象的引用或者指针的返回的生命周期从函数调用开始一直到调用结束后,对象被销毁,此后再对其进行一些操作会产生未定义行为(undefined behavior)。
修改:
#include <iostream>int& fun(){static int x = 3;return x;}int* fun1(){static int x = 3;return &x;}int main(){int& ref = fun();int* ptr = fun1();}
关键字static作用后,x变成局部静态变量,其生命周期变为从fun()开始调用,x被声明定义一直到整个程序结束,才会被销毁,因此引用变量ref绑定到x上,不会发生一些未定义行为,自动对象返回类型为指针也是这样的。
返回值优化( RVO )—— C++17 对返回临时对象的强制优化 -fno-elide-constructors会使编译器不再进行返回值优化(RVO)
CMakeLists.txt内容(注意此时标准为C++14):
cmake_minimum_required(VERSION 3.21)project(project0_0)set(CMAKE_CXX_STANDARD 14)add_executable(project0_0 main.cpp)add_definitions(-fno-elide-constructors)
#include <iostream>struct Str{Str() = default;Str(const Str&){std::cout << "Copy constructor is called" << std::endl;}};Str fun(){Str val;return val; //具名返回值优化}int main(){Str res = fun();}
编译结果:
D:\codework\project0.0\cmake-build-debug\project0_0.exeCopy constructor is calledCopy constructor is called进程已结束,退出代码0
#include <iostream>struct Str{Str() = default;Str(const Str&){std::cout << "Copy constructor is called" << std::endl;}};Str fun(){return Str{};//非具名返回值优化}int main(){Str res = fun();}
编译结果:
D:\codework\project0.0\cmake-build-debug\project0_0.exeCopy constructor is calledCopy constructor is called进程已结束,退出代码0
当C++标准为C++14时,没有-fno-elide-constructors,两段代码的编译结果都输出为空
当C++17时,加上-fno-elide-constructors,对第一段代码来说,其编译结果如下:
D:\codework\project0.0\cmake-build-debug\project0_0.exeCopy constructor is called进程已结束,退出代码0
对第二段代码来说,其编译结果如下:
D:\codework\project0.0\cmake-build-debug\project0_0.exe进程已结束,退出代码0
可见C++17标准下,对于临时对象的返回值优化是强制的,即使有-fno-elide-constructors,输出仍然为空;同时,对于具名返回值(也就是第一段代码)有一定的优化。
返回类型表示了函数计算结果的类型,可以为 void
使用 constexpr if 构造具有不同返回类型的函数
#include <iostream>constexpr bool flag = true;auto fun(){if constexpr(flag){return 3;}else{return 1.5;}}int main(){}
返回类型与结构化绑定( C++ 17 )
#include <iostream>#include <string>struct Str{int x;std::string y;};Str& fun(){static Str inst;return inst;}int main(){auto& [v1,v2] = fun();}
[[nodiscard]] 属性(C++17)
#include <iostream>[[nodiscard]] int fun(int a,int b){return a + b;}int main(){fun(1,2);}
在Clion中的警告:
Ignoring return value of function declared with 'nodiscard' attribute
因此可以使用[[ nodiscard ]]属性指示在执行函数调用时不应忽略函数的返回值。如果返回值被忽略,编译器应该对此发出警告。
