C++变得越来越像Python的证据

来源:互联网 发布:C1oracle数据库查询慢 编辑:程序博客网 时间:2024/04/30 01:11

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


C++ Has Become More Pythonic

C++ has changed a lot in recent years. The last two revisions, C++11 and C++14, introduce so many new features that, in the words of Bjarne Stroustrup, “It feels like a new language.”

It’s true. Modern C++ lends itself to a whole new style of programming – and I couldn’t help noticing it has more of a Python flavor. Ranged-based for loops, type deduction, vector and map initializers, lambda expressions. The more you explore modern C++, the more you find Python’s fingerprints all over it.

Was Python a direct influence on modern C++? Or did Python simply adopt a few useful constructs before C++ got around to it? You be the judge.

Literals

Python introduced binary literals in 2008. Now C++14 has them. [Update: Thiago Macieira points out in the comments that GCC actually supported them back in 2007.]

static const int primes = 0b10100000100010100010100010101100;

Python also introduced raw string literals back in 1998. They’re convenient when hardcoding a regular expression or a Windows path. C++11 added the same idea with a slightly different syntax:

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

Range-Based For Loops

In Python, a for loop always iterates over a Python object:

for x in myList:    print(x)

Meanwhile, for nearly three decades, C++ supported only C-style for loops. Finally, in C++11, range-based for loops were added:

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

You can iterate over a std::vector or any class which implements the begin and end member functions – not unlike Python’s iterator protocol. With range-based for loops, I often find myself wishing C++ had Python’s xrange function built-in.

Auto

Python has always been a dynamically typed language. You don’t need to declare variable types anywhere, since types are a property of the objects themselves.

x = "Hello world!"print(x)

C++, on the other hand, is not dynamically typed. It’s statically typed. But since C++11 repurposedthe auto keyword for type deduction, you can write code that looks a lot like dynamic typing:

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

When you call functions that are overloaded for several types, such as std::ostream::operator<<or a template function, C++ resembles a dynamically typed language even more. C++14 further fleshes out support for the auto keyword, adding support for auto return values and auto arguments to lambda functions.

Tuples

Python has had tuples pretty much since the beginning. They’re nice when you need to package several values together, but don’t feel like naming a class.

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

C++ added tuples to the standard library in C++11. The proposal even mentions Python as an inspiration:

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

Python lets you unpack a tuple into separate variables:

x, y, z = triple

You can do the same thing in C++ using std::tie:

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

Uniform Initialization

In Python, lists are a built-in type. As such, you can create a Python list using a single expression:

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

C++’s std::vector is the closest analog to a Python list. Uniform initialization, new in C++11, now lets us create them using a single expression as well:

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

In Python, you can also create a dictionary with a single expression:

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

Similarly, uniform initialization also works on C++’s std::map and unordered_map:

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

Lambda Expressions

Python has supported lambda functions since 1994:

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

Lambda expressions were added in C++11:

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

In 2001, Python added statically nested scopes, which allow lambda functions to capture variables defined in enclosing functions:

def adder(amount):    return lambda x: x + amount...print(adder(5)(5))

Likewise, C++ lambda expressions support a flexible set of capture rules, allowing you to do similar things:

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

Standard Algorithms

Python’s built-in filter function lets you selectively copy elements from a list (though list comprehensions are preferred):

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

C++11 introduces std::copy_if, which lets us use a similar, almost-functional style:

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

Other C++ algorithms that mimic Python built-ins include transformany_ofall_ofmin and max. The upcoming ranges proposal has the potential to simplify such expressions further.

Parameter Packs

Python began supporting arbitrary argument lists in 1998. You can define a function taking a variable number of arguments, exposed as a tuple, and expand a tuple when passing arguments to another function:

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

C++11 adds support for parameter packs. Unlike C-style variable arguments, but like Python’s arbitrary argument lists, the parameter pack has a name which represents the entire sequence of arguments. One important difference: C++ parameter packs are not exposed as a single object at runtime. You can only manipulate them through template metaprogramming at compile time.

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

Not all of the new C++11 and C++14 features mimic Python functionality, but it seems a lot of them do. Python is recognized as a friendly, approachable programming language. Perhaps some of its charisma has rubbed off?

What do you think? Do the new features succeed in making C++ simpler, more approachable or more expressive?

« Fixing GCC's Implementation of memory_order_consume

Comments (29)

's avatar - Go to profile

Patrice Roy· 6 days ago

From memory, Dave Abrahams made similar remarks as C++11 was stabilizing. I don't know if his texts are still online, sadly, but it became more and more clear that the simplification of syntax led to something we can call «Pythonic». In particular, the auto-deduced function return types and arguments, which help tremendously in writing generic lambdas, were among the things he suggested and showed to be feasible and valuable. 

A student of mine, a few years ago, remarked an interesting trend where, in his opinion, dynamic languages have a tendency to make their type systems more explicit (one could think of things such as TypeScript here, or some planned enhancements to JavaScript) while languages with static type systems have a tendency to offer syntax simplifications (auto, var, lambdas, etc.); he saw both «families» converging towards one another; I think he was onto something :) 

Interesting post, as always. Cheers!
Reply
1 reply · active 2 days ago
Francisco Lopes's avatar

Francisco Lopes· 6 days ago

The raw string literal syntax is not right. Nice article.
Reply
Martins's avatar

Martins· 6 days ago

Raw string literal example for C++11 is a bit wrong. It should have uppercase R, not lowercase r. And it should include parenthesis after and before " symbols: 
R"(c:\this\string\has\backslashes)";
Reply
1 reply · active 6 days ago
Jeff Preshing's avatar - Go to profile

Jeff Preshing· 6 days ago

Thanks. Fixed now!
Reply
passerby's avatar

passerby· 6 days ago

I think it's more of ML influence than Python.
Reply
Stan's avatar

Stan· 6 days ago

Does C++ have the useful Python feature that the assignment operator can be used in a function call so that the order of the arguments passed does not matter (matters less) by mapping the argument to the parameter variable name with the equal sign. This is not the default parameter feature that both C++ and Python have but something else.
Reply
1 reply · active 5 days ago
Guest's avatar

Guest· 5 days ago

No, but there is a [proposal] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4172.htm) to add that feature. In the mean-time, if you really need named parameters, you can use the [boost library](http://www.boost.org/doc/libs/1_57_0/libs/parameter/doc/html/index.html).
Reply
's avatar - Go to profile

fenbf· 6 days ago

in Visual Studio 2015 Preview there is even "Terse Range-Based For Loops" 

basically you will be able to write: "for (elem : range)" 

propsed for C++17
Reply
4 replies · active 5 days ago
Budai's avatar

Budai· 6 days ago

The terse form has been voted out, so its auto&& from now on.
Reply
's avatar - Go to profile

fenbf· 5 days ago

In favor: 8 Opposed: 43 abstain: 18 

I thought it would pass since it was even implemented in VS...
Reply
fabio's avatar - Go to profile

fabio· 6 days ago

As an inside view from the committee I would say it is really more that python was faster in getting all the good stuff. 
Other languages usage of a certain feature is seldom viewed as more than a hint that a feature might be a good idea, most features are motivated by solving problems that were Identified. Individual feature authors and reviewers might however be influenced by other languages they know/like. 
As far as language influence goes I would say that Haskell () is the strongest one, it keeps popping up as a reference during design discussions. 
Python seems to be regarded as being good enough so that saying: "We should do x because Python does so" will not get you thrown from the room, unlike other languages I won't mention.
Reply
John's avatar

John· 6 days ago

Actually since python barely has any exclusive feature that weren't present in older languages like Common Lisp shouldn't we say that c++ has become more lispy?
Reply
4 replies · active 3 days ago
Ted's avatar

Ted· 6 days ago

This. Lisp had much of the functionality that is liked about python.
Reply
Catalin's avatar

Catalin· 6 days ago

Lisp again ... a great language killed only by it's atrocious syntax. List is powerful but not Pythonic in any way.
Reply
knivil's avatar

knivil· 5 days ago

The title could be also: C++ has become more <Lisp/Scheme/Javascript/Haskell/...> . And Python is very similar to Lisp or Scheme. It has also something similar to call/cc. Maybe C++ will get something similar with resumable functions. And yes, I do not like Python.
Reply
's avatar - Go to profile

jfcaron· 6 days ago

There's even an internet aphorism regarding this: 
https://en.wikipedia.org/wiki/Greenspun%27s_tenth...
Reply
rightfold's avatar

rightfold· 5 days ago

Declaring variables and declaring their types are orthogonal. In JavaScript, for example you can declare variables but you cannot declare their types.
Reply
Lucidguppy's avatar

Lucidguppy· 5 days ago

I think this is a great idea for a book. It would give Pythonistas a good resource to write pythonic c++.
Reply
hammonjj's avatar - Go to profile

hammonjj· 5 days ago

A major difference between auto and python's variable declarations is that auto is a compile time deduction, so it isn't really dynamically typed.
Reply
1 reply · active 4 days ago
Jeff Preshing's avatar - Go to profile

Jeff Preshing· 4 days ago

That is what I meant by "C++, on the other hand, is not dynamically typed."
Reply
Thiago Macieira's avatar

Thiago Macieira· 5 days ago

GCC has supported binary literals as an extension since 2007 in both C and C++ modes. So we should instead say that Python adopted it after C and C++ had established the precedent. 

The text about variable arguments is a bit misleading. Variable arguments to functions in general have been supported in C since the dawn of time (remember printf). Variable arguments to macros have been available since C99 and GCC implemented them as an extension to C++ before C++11 standardised them. C++11 only added variable template parameters, which is just one of the aspects, albeit a very, very important one.
Reply
1 reply · active 4 days ago
Jeff Preshing's avatar - Go to profile

Jeff Preshing· 4 days ago

I think a lot of C++ programmers still use printf (or some variant thereof), so I thought it wasn't necessary to mention the old syntax. Still, I've revised the article a bit based on your feedback. Thanks.
Reply
Jochen's avatar

Jochen· 1 day ago

Great article. IMHO it doesn't matter if C++ is now influenced by python or not. What the article shows is, that it is possible to mimic the python way. But as it is also obvious by all this exmaples: To write python code is easy, straight forward and .... most important: concise - which also obvious C++ was and still is not. Just look at the first example of lambda expressions. In C++ it's littered with syntax instead of focusing on the task to achieve.
Reply
olimp's avatar

olimp· 13 hours ago

This article doesn't show that C++ became more Pythonic. It rather shows that C++ has became more Rubyish. Or Lispish. Or Javascriptish in some ways. Well, simply put - it included some features that some other languages had before. It wasn't really influenced by Python more than any other language. :-)

0 0