C++ Has Become More Pythonic

目录
  1. Literals
  2. Range-Based For Loops
  3. Auto
  4. Tuples
  5. Uniform Initialization
  6. Lambda Expressions
  7. Standard Algorithms
  8. Parameter Packs

译自:http://preshing.com/20141202/cpp-has-become-more-pythonic

近些年C++ 发生了很多变化。最新的两个版本,C++11C++14引入了如此多的新特性,正如Bjarne Stroustrup所说的:“它感觉就像一门新的语言”

确实是这样。现代C++形成了一种全新的编程风格——使我不得不注意到它带有的更多Python的味道。基范围的for循环、类型推导、vector和map的初始化、lambda表达式。随着你更深入的探索现代C++,你越会发现Python的痕迹在里面。

现代C++直接受Python的影响吗?或者仅仅是Python在C++之前使用了这些设计?由你来判断。

Literals

Python在2008年引入了二进制字面值。现在C++14拥有了它们

1
static const int primes = 0b10100000100010100010100010101100;

Python早在1998年引入了原始字符串字面值,它们在硬编码正则表达式或Windows路径时很方便。C++11使用略微不同的语法加入了同样的想法:

1
const char* path = R"(c:\this\string\has\backslashes)";

Range-Based For Loops

在Python中,for循环总是在一个Python对象上迭代:

1
2
for x in myList:
print(x)

与此同时,将近30年,C++只支持C风格的for循环。最终,在C++11中,基于范围的for循环被添加:

1
2
for (int x : myList)
std::cout << x;

Auto

Python一直是一种动态类型语言。任何地方,你都不用声明变量的类型,因为类型是对象自己的属性。

1
2
x = "Hello world!"
print(x)

另一方面,C++不是动态类型的,它是静态类型语言。但是从C++11使用`auto’关键字进行类型推导以来,你能够看起来像动态类型一样来编写代码:

1
2
auto x = "Hello world!";
std::cout << x;

当你调用那些被好几种类型重载的函数,例如std::ostream::operator<<,或模板函数的时候,C++就更表现出动态类型语言的特征。C++14更进一步扩充了对auto关键字的支持,增加了对auto返回值
lambda函数使用auto参数的支持。

Tuples

Python几乎从开始就支持tuple。当你需要把几个值打包在一起的时候表现的非常友好,而不需要命名一个类。

1
2
triple = (5,6,7)
print(triple[0])

C++在C++11中把tuple添加到了标准库中。在这一提议中甚至提到是从Python中得到的灵感:

1
2
auto triple = std::make_tuple(5,6,7);
std::cout << std::get<0>(triple);

Python可以让你解包一个tuple到分开的变量中:

1
x,y,z = triple

你在C++中使用std::tie也可以做到同样的事情:

1
std::tie(x,y,z) = triple;

Uniform Initialization

在Python中,list是一个内建类型。这样,你可以使用单一的表达式来创建一个Python列表:

1
2
myList = [6,3,7,8]
myList.append(5);

C++中的std::vetcor是Python列表的最相近的模仿。在C++11中新添加了统一初始化,现在也可以让我们使用单一的表达式来创建它们:

1
2
auto myList = std::vector<int>{6,3,7,8};
myList.push_back(5);

在Python中,你也可以使用单一的表达式创建一个字典

1
2
myDict = {5: "foo", 6: "bar"};
print(myDict[5])

同样的,统一初始化也可以工作在C++的std::mapunordered_map

1
2
auto myDict = std::unordered_map<int, const char*>{{5, "foo"}, {6, "bar"}};
std::cout << myDict[5];

Lambda Expressions

自从1994年开始Python就支持了lambda函数

1
myList.sort(key = lambda x: abs(x))

lambda表达式C++11中被添加:

1
std::sort(myList.begin(), myList.end(), [](int x, int y){return std::abs(x) < std::abs(y);});

在2001年,Python增加了静态嵌套作用域,它可以使得lambda函数捕获在外围函数中定义的变量:

1
2
3
4
def adder(amount):
return lambda x: x + amount
...
print(adder(5)(5))

同样的,C++的lambda表达式支持一套灵活的捕获规则,使你能够做到相似的事情:

1
2
3
4
5
auto adder(int amount) {
return [=](int x){return x + amount;};
}
...
std::cout << adder(5)(5);

Standard Algorithms

Python的内建filter函数使你从列表有选择性的拷贝元素(但推荐使用列表推导):

1
result = filter(lambda x: x >= 0, myList)

C++11引入了std::copy_if,它使我们可以使用相似的,几乎是函数式的风格:

1
2
auto result = std::vector<int>{};
std::copy_if(mylist.begin(), myList.end(), std::back_inserter(result), [](int x){return x >= 0;});

其它C++算法借鉴了Python内建的transformany_ofall_ofminmax函数。即将到来的rangs提议将更进一步的简化这些表达式。

Parameter Packs

Python在1998年开始支持任意参数列表。你可以定义一个函数,它可以接受可变数量的参数。这些参数表现为一个tuple,当把它们传给另一个
函数时tuple会被展开:

1
2
3
4
def foo(*args):
return tuple(*args)
...
triple = foo(5,6,7)

C++11添加了对参数包的支持。不同于C风格的可变参数,它和Python的任意参数列表很像,参数包有一个名字来代表整个参数序列。一个重要的区别在于:C++的参数包在运行时并不表现为一个单一的对象。你只有通过模板元编程技术在在编译时来操纵它们。

1
2
3
4
5
template <typename... T> auto foo(T&&... args) {
return std::make_tuple(args...);
}
...
auto triple = foo(5,6,7);

并非所有C++11C++14的新特性都借鉴于Python,但其中很大一部分看起来似乎如此。Python被认为是一个友好的、平易近人的编程语言,或许它的一些魅力已经开始暗淡了?

你觉得如何?C++的这些新特性是否使C++变得更简单、更平易近人或者更富有表达力呢?

评论