其他特性
Published on
nullptr
C++11以前通常用NULL表示空指针,但是NULL是用#define定义的,通常定义为0:
#define NULL 0
因此实际上它与数值0没有区别,这在重载时会引发混乱。如果定义以下两个重载函数:
void foo(char *);
void foo(int);
在用NULL作为参数调用时:
foo(NULL);
用户应该希望调用char*版本,然而由于NULL实际上是0,会导致调用int版本。为了解决这一问题,C++11引入了nullptr关键字用来专门表示空指针。它是std::nullptr_t类型的纯右值,可以隐式转换为任何指针类型及任何成员指针类型。使用nullptr调用上述函数:
foo(nullptr);
可以正确调用char*版本。
基于范围的for循环
C++11引入了基于范围的for循环,它相当于其它语言的for-each或for-in循环,可以使循环代码更加简洁。
std::vector<int> arr(5, 100);
for (auto &i : arr)
{
std::cout << i << std::endl;
}
条件表达式中支持初始化语句
c++17中支持在if或者switch语句中进行初始化,这个能力的出现能够让代码更加简洁。
std::map<int, std::string> c = {{1, "a"}};
if (auto res = c.insert(std::make_pair(2, "b")); !res.second)
{
std::cout << "key 1 exist" << std::endl;
}
else
{
std::cout << "insert success, value:" << res.first->second << std::endl;
}
非成员函数std::begin()与std::end()
C++标准库中的容器都实现了begin()与end()成员函数,用于获取首尾迭代器。例如:
std::vector<int> v;
auto b = v.begin();
auto e = v.end();
但是对于非标准库中的其它可迭代对象,如数组或用户自定义类型,则不能使用begin()与end()成员函数。此时可以使用非成员函数std::begin()与std::end()实现同样的功能:
int arr[10];
auto b = std::begin(arr);
auto e = std::end(arr);
对于标准库中的容器,调用成员函数与非成员函数的作用完全相同。因此,推荐在所有需要迭代的情况下使用非成员函数,这种做法更加通用,且不会出错。
变量模板
C++14允许通过变量模板定义一族变量:
// 声明变量模板
template <class T>
constexpr T pi = T(3.1415926535897932385L);
// 在函数模板中使用对应的变量
template <class T>
T circular_area(T r)
{
return pi<T> * r * r;
}
在类作用域中使用时,变量模板声明一个静态数据成员模板:
using namespace std::literals;
struct matrix_constants
{
template <class T>
using pauli = hermitian_matrix<T, 2>; // 别名模版
template <class T>
static constexpr pauli<T> sigmaX = {{0, 1}, {1, 0}}; // 静态数据成员模板
template <class T>
static constexpr pauli<T> sigmaY = {{0, -1i}, {1i, 0}};
template <class T>
static constexpr pauli<T> sigmaZ = {{1, 0}, {0, -1}};
};
与其他静态成员一样,静态数据成员模板也需要一个定义。这种定义可以在类定义外提供:
struct limits
{
template <typename T>
static const T min; // 静态数据成员模板的声明
};
template <typename T>
const T limits::min = {}; // 静态数据成员模板的定义
template <class T>
class X
{
static T s; // 类模板的非模板静态数据成员的声明
};
template <class T>
T X<T>::s = 0; // 类模板的非模板静态数据成员的定义
inline变量
inline变量可以让变量有多于一次的定义。C++17之前,我们定义全局变量,总需要将变量定义在cpp文件中,然后再通过extern关键字来告诉编译器,这个变量已经在其他地方定义过了。inline变量出现后,我们可以直接将全局变量定义在头文件中,而不用担心出现redefined错误信息。
// test.h
inline void print()
{
std::cout << "hello world" << std::endl;
}
inline int num = 0;
// func.h
#include "test.h"
inline void add(int arg)
{
num += arg;
print();
}
// main.cpp
#include "func.h"
int main()
{
num = 0;
print();
add(10);
}
嵌套命名空间
// 传统写法
namespace A
{
namespace B
{
namespace C
{
};
};
};
// 新写法
namespace A::B::C
{
};
自定义字面值(自定义后缀运算符)
在C++11中,可以在字面值的后面加一些后缀,将它们转换为所需要的值。例如:
// 90度角对应的弧度值
double x = 90.0_deg;
这是通过定义以下后缀运算符实现的:
constexpr long double operator"" _deg(long double deg)
{
return deg * 3.14159265358979323846264L / 180;
}
后缀运算符的参数只能是以下类型:
- const char *
- unsigned long long int
- long double
- char
- wchar_t
- char8_t
- char16_t
- char32_t
- const char *, std::size_t
- const wchar_t *, std::size_t
- const char8_t *, std::size_t
- const char16_t *, std::size_t
- const char32_t *, std::size_t
其返回值类型不受限制,因此可以用于返回自定义类型的字面值。