0%

C++14小结

继上次的C++11的小结之后,很久没写些东西了(主要是懒…),那么这一次是C++14的小结。
C++14 作为一个小版本,功能上更像C++11的一个扩展版本,大部分是对11版本的一个完善。

语言特性

函数返回值推导

C++11标准中新增了类型推导功能,增加了auto和decltype关键字,但是在C++11标准中并不支持将返回值声明为auto,14标准扩展了该功能,14标准后可以将所有函数包括lambda表达式的返回值声明为auto。

1
auto Func();

即返回值的类型交由编译器去推导,这里需要注意的是如果函数里有多处返回值,需要保证这些返回值可以被推断为相同类型。

自动推导类型的声明

同样是decltype和auto引发的问题,auto偏向于将变量推导为非引用类型变量,auto&&偏向于将变量推导为引用类型,在模板编程中如果要声明一个和原变量相同引用属性的变量,这在之前的版本中较为困难,所以在14标准中新增了decltype(auto)语法。

1
decltype(auto) i = a;

如上使用i和a将保持相同的引用属性。

更宽松的constexpr限制

11标准中新增了constexpr这个关键字用于表明常量表达式,但是在11标准中constexpr函数体基本上为一句return语句,这在使用上十分不便,14标准中放宽了constexpr限制,现在constexpr可以包含以下内容:

  • 任何非static和非thread_local的变量声明,但是这些变量必须赋初值
  • 条件分支if和switch语句,但是goto语句不允许在函数体里出现(为什么到现在还有人喜欢用goto)
  • 循环语句for、while
  • 对在constexpr函数体内声明变量的赋值,以及任何constexpr函数调用,这里需要注意的是11标准中constexpr的声明,将隐式的将函数声明为const,这个规则在14标准中废除,同时现在constexpr可以调用非const的成员函数。(这里测试时发现constexpr函数体内可以对成员变量和static变量赋值,不知道是不是编译器bug,不过算起来,既然可以调用non const函数,那函数体内给成员变量赋值按理也应该可以才是。测试环境 gcc 9.2.0 --std=c++14)

变量模板

14标准中新增了一个看上去比较诡异的模板,变量模板;顾名思义,与函数模板、类型模板一样,现在可以定义变量的模板,一个比较经典的用法是关于圆周率:

1
2
3
4
5
6
template<T>
constexpr T pi = T(3.141592653589793238462643383);

// 特化
template<>
constexpr const char* pi<const char*> = "pi";

二进制字面量

14标准中增加了二进制字面量,使用前缀0b0B来表示。

数字分位符

14标准中新增了单引号作为数字的分位符,如:

1
2
3
4
auto integer_literal = 100'0000;
auto floating_point_literal = 1.797'693'134'862'315'7E+308;
auto binary_literal = 0b0100'1100'0110;
auto silly_example = 1'0'0'000'00;

泛型lambda

14标准中可以使用auto来声明lambda参数,如:

1
auto lambda = [](auto x, auto y) {return x + y;}

同时泛型lambda遵循函数模板的参数推导规则,以上lambda等同于

1
2
3
4
5
struct
{
template<typename T, typename U>
auto operator()(T x, U y) const { return x + y; }
} lambda{};

Lambda捕获表达式

依然是lambda的规则扩充,对于外部传给lambda函数内部的变量在11标准中可以是按值或者按引用进行传递,但对于11中新增的右值引用却无法传递。14标准中扩展了lambda的捕获规则,现在可以使用形如下面的初始化方式来捕获外部变量

1
2
3
4
5
{
int i = 1;
int j = 2;
auto func = [i = 3, j = std::move(j)] (auto x) { return x+ i + j; };
}

捕获列表中的i和j并不是外部的整型,准确说是func这个lambda中使用到int和int&&,他们覆盖了外部的i和j,而他们的类型是经由编译器自动推导得到的。

deprecated属性

与Java和C#类似,14标准中新增了deprecated属性用于标识该函数已经弃用或者进行相应警告:

1
2
3
4
5
6
7
8
9
10
11
[[deprecated]]
int t()
{
//...
}

[[deprecated("g() 不是线程安全的")]]
int g()
{
//...
}

在编译期如果有使用到deprecated的方法时,编译器将发出相应警告,当然也可以通过编译选项关掉这些警告信息,gcc可以通过-Wno-deprecated-declarations关闭。

STL更新

共享互斥量

14标准中新增了std::shared_timed_mutex共享互斥量,位于新头文件<shared_mutex>中,通常用于多个读线程但是只有一个写线程的场景,关于<mutex>和<shared_mutex>的内容后面再写篇文章详细记录下。

关联容器的异构查找

14标准中给四种标准关联容器(map、multi_map、set、multi_set)的find函数新增了两个重载版本的find函数模板:

1
2
3
template< class K > iterator find( const K& x );

template< class K > const_iterator find( const K& x ) const;

K必须是能和查找的key能进行等价比较的类型,新增该异构查找方法的原因个人认为主要是避免由于隐式转换带来的开销。

标准自定义字面量

14标准中为stl中的类型增加了标准字面量后缀:

  • “s”,用于创建std::basic_string
  • “h”, “min”, “s”, “ms”, “us”, “ns” 用于创建相应的std::chrono::duration时间间隔
  • “if”, “i”, “il” 用于创建相应的std::complex<float>, std::complex<double>和std::complex<long double>

如:

1
2
3
auto str = "hello world"s; // auto deduces string
auto dur = 60s; // auto deduces chrono::seconds
auto z = 1i; // auto deduces complex<double>

多元组的类型寻址

14标准中为11标准中新增的tuple增加了get方法,现在可以通过类型来寻址tuple成员,但是如果同时有多个相同类型将会出现编译器错误:

1
2
3
4
5
6
7
8
9
10
11
template< class T, class... Types >
constexpr T& get(tuple<Types...>& t) noexcept;

template< class T, class... Types >
constexpr T&& get(tuple<Types...>&& t) noexcept;

template< class T, class... Types >
constexpr const T& get(const tuple<Types...>& t) noexcept;

template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;

如:

1
2
3
4
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t); // Same as before: j == 7
string s = get<string>(t); // Compile-time error due to ambiguity

其他较小的标准库改动

<memory>中新增了std::make_unique用于创建std::unique_ptr对象:

1
2
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );

<type_traits>中的std::integral_constant增加了operator()方法:

1
constexpr value_type operator()() const noexcept;

<utility>中新增std::integer_sequence类模板,用于表示编译期整数序列:

1
2
template< class T, T... Ints >
struct integer_sequence;

<iterator>中新增了std::cbegin/std::cend用于返回指定的const iterator,新增std::rbegin/std::rend和std::crbegin/std::crend用于返回逆序的iterator和const iterator。

1
2
3
4
5
6
7
8
template< class C >
constexpr auto cbegin( const C& c ) noexcept -> decltype(std::begin(c));

template< class C >
auto rbegin( C& c ) -> decltype(c.rbegin());

template< class C >
auto crbegin( const C& c ) -> decltype(std::rbegin(c));

<type_traits>中新增了std::is_final,用于判断是否为final类:

1
2
template< class T >
struct is_final;

<algorithm>中的std::equal、std::mismatch和std::is_permutation新增了重载函数,添加了第二个迭代器的中止位置参数:

1
2
3
template< class InputIt1, class InputIt2 >
bool equal( InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2 );

<iomanip>中新增std::quoted用于输入/输出流添加或者去除分隔符(默认为双引号,可自行指定):

1
2
template< class CharT >
/*unspecified*/ quoted(const CharT* s, CharT delim=CharT('"'), CharT escape=CharT('\\'));