Hi everybody! As I had a look on the new C++ proposal the
last time , I found many small interesting things apart form the big, important features. But this was only a first look. Recently I had another look there (and at the C++0x FAQ* by Bjarne Stroustrup as well) and discovered even more interesting crittures. So what did I miss?
1. Some SyntaxFirst of all the
new function definition syntax! Now I can write something like*:
using PF [](double) -> void;
along the lines of lambda function definition! Well, that's definitely a treat, what a relief from
typedef void (*PF) (double);
To be honest, I don't know if I've got the last one right, I have always to look thes syntax up or copy and paste from my other code! The [] notation can be generally used to say to the compiler that "the function type will be specified later"! Thusly we can write the following when usign embedded type like List::Link
[] List::erase(Link* l) -> Link* { ... } ;
We postpone the return type definition utill we have entered the scope of List, and thus no scope specifier is needed!
I liked this notation (it makes the code look a little Groovy-like), but alas, it seems that not everyone does so, as the related "auto arrow" notation proposal was rejected recently**! What is auto-arrow? Look at that***:
auto f() -> int;
typedef auto PF() -> int;
the first one is a function from void to int, and the second one is a function type definition for the same signature. Looks good enough, but consider this one:
auto *pf(auto *p() -> int) -> auto *()->int;
I don't even want to discuss what it means, if you are curious look it up in ***! So after that proposal was justly rejected, I'm a little afraid of the fate of the
[] notation - is it the next one to be scrutinized? I hope not!
2. Initalization and initializersI though I've understood the new initalizer notation, but I was surprized how ubiquitous it became! At first I though it would be only used in assignment like
vector<int> xs = {1, 2, 3 };
Not so, nowadays you can use it (i.e. the new
{} notation) everywhere, even instead of classic parentheses!
X x{a};
f({a}); // initializes a temporary
X* p = new X{a};
This all does look pretty strange to me, and to be honest I don't like it that much, but on the other side you can use it to avoid verbosity with pretty good results:
map1.insert({a, b}); // sic!
The other improvement with initialization is that we can initialize the string and integer class members directly in the definition of the class!
class X
{
int a = 7;
string s{"abcd"}; // new syntax!
};
I can only say
AT LAST and amen!!!
Belonging to the "initialization" block of features, we must mention the access to the inherited constructors (in addition to the also new feature of delegating constructors, i.e. constructors which can be invoked of another constructors of the same class):
class A { public: A(){} };
class B : public A
{
using A::A; // imports all the A's constructors into current scope!
};
// now this is possible:
B b;
At this place I'd like to mention another related feature: disallowing copy and assignment operators. You can write for example:
X& operator= (const& X) = delete;
to disallow it! Well, if I consider that Google even has a macro exaclty for that reason (
DISALLOW_COPY_AND_ASSIGN(), see
here) and that I used an UML tool to generate the private version of it for my every class I think that the default-generating them was a bad language design idea! If we want a copy semantics we'd better tell the compiler so! Hindsight is 20/20.
3. Threading etc.Of course thread, mutex, condition variables and atomics are there as basic support (as expected). On the higher level we have futures and a
packaged_task class, which wraps features for simpler usage. Moreover, Bjarne does promise that an async task launcher (logically named
async) will be included in C++0x as well. Then we could launch tasks simply like that:
auto f1{async(funcX(100))};
auto f2{async(funcX(200))};
return f1.get() + f2.get(); // waits on both futures
If he's right it would be very nice!
As GC is connected to multithreading in an intimate way (at least in my eyes, sholud blog about it some time soon!), I must state here that the
GC is still missing!
But not entirely, we've got some minimal support for third party garbage collectors in there (
20.8.13.7 Pointer safety). It defines what a "safely drived" (i.e reacheable) or an "disgused" pointer is (
3.7.4.3 Safely-derived pointers) and lets the programmer specify where such pointers can/cannot be found with
declare_reachable()/
declare_no_pointers() functions.
One thing that struck me however in the threading library classes is the
flexibility of interfaces. Take the
std::thread's constructor for example. You can use it like that ****:
void hello_func() { ... }
// say hello
std::thread t{hello_func};
or like that, using a functor:
class Greeting
{
public:
explicit Greeting(std::string const& message) { ... }
void operator()() const { ... }
void greet(std::string const& message) const { ... }
};
// say goodbye
std::thread t(Greeting("goodbye"));
or currying the thread function with
std::bind:
void greeting_func(std::string const& message) { ... }
std::thread t(std::bind(greeting_func, "hi!"));
or without
std::bind:
std::thread t(greeting_func, "hi!");
or without
std::bind and with more arguments:
void write_sum(int x, int y){ ... }
std::thread t(write_sum, 123, 456);
or using the member function pointer
Greeting x;
std::thread t(&Greeting::greet, &x, "greet");
// or by a smart pointer
std::shared_ptr<Greeting> ptr(new Greeting);
std::thread t1(&Greeting::greet, ptr, "hello");
// or by reference
std::thread t2(std::ref(x));
// or directly!
std::thread t2(x);
See, you don't need to worry about the initalization, the new constructs will just take anything you throw at it!
4. MiscellaneaFirst the
variadic templates. What a name for templates with viariable number of arguments! I guess it's an interpolation of the "dyadic" concept which just means "of two arguments" :-). But seriously, we can writhe things like
template<typename Head, typename... Tail>
doing recursion and typematching like in Clojure or Haskell ;-) Well a kind of, but it looks like that at least. Look at the n-tuple definition using variadic templates in Bjarne's FAQ*.
Further, the new and
improved enums (which are class based now) can be now
forward defined ! But I'm doing this all the time with C++ Visual Studio 2005! Ouch...
No for the geeky ones: we have
user defined literals! You can define new literal types using the following new operator type:
operator""() (plus
constexpr!), for example
"abc"s; // s: string literal
2i; // i: imaginary literal
For details have a look at Bjarne's FAQ*. NIce feature, but will I use it? It is readable for the maintainer? We'll see.
The next 2 features look to me as if they came straight from
Python. First the
raw strings:
string s = R"[ahjh/?=(%/$§%§%\w\\w///]";
Why not just
R"..."? Dunno. Next the
foreach variant of the old
for() loop.Together with initilizers we can now have some
Python feeling in C++:
vector<string> xs = {"aba", "bab", "aab" };
for(auto x:xs) cout << x; // x:xs looks even a little Haskell-like ;-))
So there are many new things, some of them making C++ look like entirely another language. No I wonder - will the C++ programmers use it, or will they stuck to the old, but known evil (like the old function definition syntax)?
---
*Bjarne's "C++0x - the next ISO C++ standard", http://www2.research.att.com/~bs/C++0xFAQ.html
** Danny Kalev: "The Rejection of the Unified Function Syntax Proposal", http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=460
*** Daveed Vandevoorde: "The Syntax of auto Declarations", http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2337.pdf
**** for completness' sake, the examples are partially taken from: http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html