C++11新特性之杂项


前十篇在这里:
C++11新特性之新类型与初始化: http://blog.guoyb.com/2016/06/18/cpp11-1
C++11新特性之类型推断与类型获取: http://blog.guoyb.com/2016/06/25/cpp11-2
C++11新特性之lambda: http://blog.guoyb.com/2016/06/30/cpp11-3
C++11新特性之容器相关特性: http://blog.guoyb.com/2016/07/09/cpp11-4
C++11新特性之智能指针: http://blog.guoyb.com/2016/08/02/cpp11-5
C++11新特性之Class: http://blog.guoyb.com/2016/08/14/cpp11-6
C++11新特性之右值引用与移动: http://blog.guoyb.com/2016/08/20/cpp11-7
C++11新特性之template: http://blog.guoyb.com/2016/08/31/cpp11-8
C++11新特性之正则表达式: http://blog.guoyb.com/2016/09/10/cpp11-9
C++11新特性之随机数库: http://blog.guoyb.com/2016/09/12/cpp11-10/


这是C++11新特性介绍的第十一部分,涉及到一些不好归类的新特性。
不想看toy code的读者可以直接拉到文章最后看这部分的总结。

类型别名声明

类似typedef,新标准中可以使用using为类型声明一个别名(alias)。

1
2
3
4
5
6
7
std::cout<<"test using alias:\n";
using HT = double;
using NAME = std::string;
HT h = 1.78;
NAME name = "Robert";
std::cout<<name<<"'s height is "<<h<<std::endl;
std::cout<<"test using alias done.\n"<<std::endl;

range for

range for语句在之前的章节中已经见识过了:

1
2
3
4
5
6
7
8
9
std::cout<<"test range for:\n";
std::string nation = "CHINA";
for(auto c : nation)
std::cout<<c<<" ";
std::cout<<"\n";
for(auto &rc : nation)
rc = tolower(rc);
std::cout<<"lower: "<<nation<<std::endl;
std::cout<<"test range for done.\n";

这里需要注意的是,在第二个例子中,range for语句中可以直接使用引用,从而修改被迭代遍历的对象本身。

另外,range for不能用于动态分配内存的数组,因为动态分配的内存中没有begin、end方法可供调用。

1
2
3
4
5
6
7
8
9
10
std::cout<<"test range for of dynamic array:\n";
int *darr = new int[5]{0, 1, 2, 3, 4};
// wrong. dynamic array don't has a begin method,
// so we can't use range for here.
//for(auto i : darr)
// std::cout<<i<<'\t';
for(size_t i = 0; i < 5; i++)
std::cout<<darr[i]<<'\t';
std::cout<<'\n';
std::cout<<"test range for of dynamic array done.\n";

新的除法舍入规则

新标准中,重新统一了除法的舍入规则。主要需要留意两数符号不同时的规则:

(-x) / y = x / (-y) = - (x / y)
x % (-y) = x % y
(-x) % y = - (x % y)

1
2
3
4
5
6
7
8
9
std::cout<<"test divide:\n";
std::cout<<"10 / 3 = "<<(10 / 3)<<"\n";
std::cout<<"-10 / 3 = "<<(-10 / 3)<<"\n";
std::cout<<"-10 / -3 = "<<(-10 / (-3))<<"\n";
std::cout<<"10 % 3 = "<<(10 % 3)<<"\n";
std::cout<<"-10 % 3 = "<<(-10 % 3)<<"\n";
std::cout<<"-10 % -3 = "<<(-10 % (-3))<<"\n";
std::cout<<"10 % -3 = "<<(10 % (-3))<<"\n";
std::cout<<"test divide done.\n"<<std::endl;

尾置返回类型

之前,我们也见识过尾置返回类型和decltype的配合使用。有时,采用尾置返回类型,代码的可读性更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int (*dummy_ret1(int i))[5]
{
static int ret1[5] = {i, i*2, i*3, i*4, i*5};
int(*pret)[5] = &ret1;
return pret;
}

auto dummy_ret2(int i) -> int (*)[5]
{
static int ret2[5] = {i+1, i+2, i+3, i+4, i+5};
int(*pret)[5] = &ret2;
return pret;
}

std::cout<<"test trailing return type:\n";
int (*arr1)[5] = dummy_ret1(1);
std::cout<<(*arr1)[0]<<'\t'<<(*arr1)[4]<<std::endl;
int (*arr2)[5] = dummy_ret2(2);
std::cout<<(*arr2)[0]<<'\t'<<(*arr2)[4]<<std::endl;
std::cout<<"test trailing return type done.\n";

使用字符串作为文件名

在新标准中,可以直接使用string作为文件名进行文件流处理,而不必要求C风格字符数组。

1
2
3
4
5
6
7
std::cout<<"test string filename:\n";
std::string filename = "regex.cpp";
std::ifstream in(filename);
std::string head_ctx;
in>>head_ctx;
std::cout<<head_ctx<<std::endl;
std::cout<<"test string filename done.\n"<<std::endl;

字符串和数值的转换

新标准中,添加了多个函数用于string和数值之间的转换。

1
2
3
4
5
6
7
8
9
10
std::cout<<"test str number cvt:\n";
int age = 15;
double weight = 137.5;
std::string str_age = std::to_string(age);
std::string str_weight = std::to_string(weight);
std::cout<<"str age: "<<str_age<<"\tstr weight: "<<str_weight<<std::endl;
int int_age = std::stoi(str_age);
double d_weight = std::stod(str_weight);
std::cout<<"int age: "<<int_age<<"\tdouble weight: "<<d_weight<<std::endl;
std::cout<<"test str number cvt done.\n"<<std::endl;

bind函数

新标准中,提供了功能更强大的参数绑定函数bind,用于替换原有的bind1st和bind2nd。

1
2
3
4
5
6
7
8
9
int add_int(int a, int b)
{

return a + b;
}

std::cout<<"test bind:\n";
auto add5 = std::bind(add_int, std::placeholders::_1, 5);
std::cout<<"add5(6): "<<add5(6)<<std::endl;
std::cout<<"test bind done.\n"<<std::endl;

std::placeholders::_1表示占位符,指代第1个参数,等待后续真正调用时由用户传入。

显式类型转换

新标准中,可以指定一个类型转换为显式(explicit)的。指定为显式的类型转换,不能再进行隐式转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class OtherType
{
public:
OtherType(int i) : val(i){}
explicit operator int() {return val;}
explicit operator bool() {return val != 0;}
private:
int val;
};

std::cout<<"test explicit type cvt:\n";
OtherType c(10);
//int i = c + 10; // wrong. can't implicit cvt c to int.
int j = static_cast<int>(c) + 10;
std::cout<<"OtherType(10) + 10: "<<j<<"\n";
if(c)
{
std::cout<<"OtherType can be cvt to bool implicitly in if clause.\n";
}
std::cout<<"test explicit type cvt done.\n"<<std::endl;

这其中有一个例外,即,即使指定一个类型和bool类型之间的转换是显式的,在if语句中,也仍然可以执行隐式类型转换。

内联命名空间

新标准中引入了内联命名空间。内联命名空间内的名字可以不加命名空间名字前缀,直接在外层命名空间中使用。这为我们设置一个默认的命名空间提供了方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
inline namespace InlineSpace
{
int inline_val1 = 1;
}

namespace InlineSpace
{
int inline_val2 = 2;
}

namespace NormalSpace
{
int normal_val3 = 3;
}

std::cout<<"test inline namespace:\n";
std::cout<<"inline vals: "<<inline_val1<<'\t'<<inline_val2<<std::endl;
//std::cout<<"normal vals: "<<normal_val3<<std::endl;
std::cout<<"normal vals: "<<NormalSpace::normal_val3<<std::endl;
std::cout<<"test inline namespace done.\n"<<std::endl;

只需在第一次声明内联命名空间时加上inline关键字,之后就可以省略了。

限定作用域的enum

新标准中,可以通过enum class foo这样的形式定义一个限定作用域的枚举,其中的枚举变量在作用域之外不可见。

1
2
3
4
5
6
7
8
9
10
std::cout<<"test scoped enum:\n";
enum class number {one, two, three};
enum nation {CHN, USA, FRA};
enum nation n1 = CHN;
//enum nation n2 = nation::USA;
std::cout<<"unscoped enum: "<<n1<<'\t'<<'\n';
//number num1 = one;
number num2 = number::two;
std::cout<<"scoped enum: "<<(int)num2<<'\n';
std::cout<<"test scoped enum done.\n";

其中,one、two、three就只能在number限定的作用域中可见,而CHN、USA、FRA就没有这个限制。

指定枚举的数据类型

新标准中,可以指定枚举的数据类型为除了int之外的其他整数类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::cout<<"test enum type declare:\n";
enum long_enum
{
firstl = LLONG_MAX - 1,
secondl = ULLONG_MAX,
};
enum longlong_enum : long long
{
firstll = LLONG_MAX - 1,
secondll = LLONG_MAX
//secondll = ULLONG_MAX
};
std::cout<<firstl<<'\n'<<secondl<<'\n';
std::cout<<firstll<<'\n'<<secondll<<'\n';
std::cout<<"test enum type declare done.\n";

输出

整个测试程序的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
test using alias:
Robert's height is 1.78
test using alias done.

test range for:
C H I N A
lower: china
test range for done.
test divide:
10 / 3 = 3
-10 / 3 = -3
-10 / -3 = 3
10 % 3 = 1
-10 % 3 = -1
-10 % -3 = -1
10 % -3 = 1
test divide done.

test trailing return type:
1 5
3 7
test trailing return type done.
test string filename:
#include
test string filename done.

test str number cvt:
str age: 15 str weight: 137.500000
int age: 15 double weight: 137.5
test str number cvt done.

test bind:
add5(6): 11
test bind done.

test range for of dynamic array:
0 1 2 3 4
test range for of dynamic array done.
test explicit type cvt:
OtherType(10) + 10: 20
OtherType can be cvt to bool implicitly in if clause.
test explicit type cvt done.

test inline namespace:
inline vals: 1 2
normal vals: 3
test inline namespace done.

test scoped enum:
unscoped enum: 0
scoped enum: 1
test scoped enum done.

test enum type declare:
9223372036854775806
18446744073709551615
9223372036854775806
9223372036854775807
test enum type declare done.

总结

  1. 类似typedef,新标准中可以使用using为类型声明一个别名(alias)。
  2. range for语句可以方便的迭代对象,并且支持引用迭代。
  3. range for不能用于动态分配内存的数组。
  4. 新标准中,重新统一了除法的舍入规则。主要需要留意两数符号不同时的规则:

    (-x) / y = x / (-y) = - (x / y)
    x % (-y) = x % y
    (-x) % y = - (x % y)

  5. 可以采用尾置返回类型,提高代码的可读性。
  6. 可以直接使用string作为文件名进行文件流处理,而不必要求C风格字符数组。
  7. 添加了多个函数用于string和数值之间的转换。
  8. 提供了功能更强大的参数绑定函数bind,用于替换原有的bind1st和bind2nd。
  9. 可以指定一个类型转换为显式(explicit)的。指定为显式的类型转换,不能再进行隐式转换。有一个例外,即,即使指定一个类型和bool类型之间的转换是显式的,在if语句中,也仍然可以执行隐式类型转换。
  10. 引入了内联命名空间。内联命名空间内的名字可以不加命名空间名字前缀,直接在外层命名空间中使用。
  11. 可以通过enum class foo这样的形式定义一个限定作用域的枚举,其中的枚举变量在作用域之外不可见。
  12. 可以指定枚举的数据类型为除了int之外的其他整数类型。

完整代码详见misc.cpp

转载请注明出处: http://blog.guoyb.com/2016/09/18/cpp11-11/

欢迎使用微信扫描下方二维码,关注我的微信公众号TechTalking,技术·生活·思考:
后端技术小黑屋

Comments