Concepts were supposed to be an important new feature in C++0x. They were meant to allow programmers to specify properties (like constraints) for templates, allow compilers to do some optimization and tools to do some formal checking on the code. After years of debate, the standard committee found them “untried, risky and controversial” and ruled them out last month during their meeting in Frankfurt.

Danny Kalev, a former member of the C++ standard committee, wrote about this controversial removal, and later interviewed Bjarne Stroustrup about the concepts and the future of C++. You can read this interview, published on DevX.com, here.

You can find more about concepts in this paper by Bjarne Stroustrup.

, , , Hits for this post: 9673 .

The new C++ standard defines a new keyword, static_assert, that is already available in Visual Studio 2010 CTP. This new feature allows introducing compile time asserts. It takes an expression that can evaluate to bool and a string. If the expression evaluates to false, the compiler issues an error with the given string literal. If the expression evaluates to true, static_assert has no effect.

Here is an example for using static_assert. Suppose you want to create a template vector class, but you don’t want to allow vectors with a size smaller than 4. Then you can use a static assertion to enforce that.

template < class T, int Size >
class Vector
{
   static_assert(Size > 3, "Vector size is too small!");

   T m_values[Size];
};

int _tmain(int argc, _TCHAR* argv[])
{
   Vector< int, 4 > four;
   Vector< short, 2 > two;

   return 0;
}

Compiling this program triggers an error at the second declaration.

c:\projects\cpp_demo\cpp_demo.cpp(17) : error C2338: Vector size is too small!
  c:\projects\cpp_demo\cpp_demo.cpp(33) : see reference to class template instantiation 'Vector< T,Size >'
  being compiled
  with
  [
     T=short,
     Size=2
  ]

Most of the use cases for this feature, in my opinion, would be to test on the size of different types. For instance this sample is taken from the C++0x draft.

static_assert(sizeof(long) >= 8, "64-bit code generation required for this library.");

To me, static_assert looks like a niche feature. It would have been great if this could be used together with some other features to enfore compile time constraints on types. For instance, I want to restrict the possible types for a template class or function to only those that are derived from IListener (a fictive class).

template < class T >
class foo
{
   static_assert(convertible_from< IListener >(decltype(T)), "Type is not a correct type!");
};

Perhaps a future standard version will offer support for such things.

, , , , , Hits for this post: 16259 .

The new C++0x standard adds lambda expressions to the language. Visual Studio 2010 CTP is already supporting this new feature that brings functional techniques to C++ too.

What is a lambda expression? It’s basically a function. In F# it’s an anonymous function, in C# it’s an anonymous delegate; in C++ it’s in fact an anonymous function object. Whenever you create a new lambda function, the compiler creates a function object for you.

int main()
{
   auto l_pow2 = [](int n) {return n*n;};

   std::cout << "5 pow 2 = " << l_pow2(5) << std::endl;
}

That is equivalent to:

struct LambdaFunctor
{
   int operator()(int n) const
   {
      return n * n;
   }
};

int main()
{
   LambdaFunctor l_pow2;
   std::cout << "5 pow 2 = " << l_pow2(5) << std::endl;
}

Of course, the lambda functor can be more complicated, when the lambda function captures state from the local scope. However, that is beyond the scope of my post. I recommend that you read more about lambdas in C++ on the VC++ blog.

Question is, what are these lambdas good for? Well, they mostly come in handly with algorithms that take predicates (function objects) as arguments. I'll try to give you some examples in this post.

Let's first consider a filter function, that takes a sequence (vector of T) and a predicate that indicates what values should be filtered, and returns a new sequence. That would look like this:

template < class T >
std::vector< T > Filter(const std::vector< T >& sequence,
                        std::tr1::function< bool (T) > predicate)
{
   std::vector< T > result;

   for(auto it = sequence.begin(); it != sequence.end(); ++it)
      if(predicate(*it))
         result.push_back(*it);

   return result;
}

We can use this filter function to extract the odds numbers from a sequence (vector).

#include < iostream >
#include < vector >
#include < algorithm >
#include < functional >

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
   std::vector< int > nums;
   for(int i = 0; i < 10; ++i)
      nums.push_back(i);

   // get the odds numbers
   std::vector< int > odds = Filter< int >(nums, [](int i) {return (i % 2) == 1;});

   // print the new sequence
   for_each(odds.begin(), odds.end(), [](int n){std::cout << n << std::endl;});

   return 0;
}
1
3
5
7
9

You could see in the above example that a second lambda function is used for printing the numbers to the console.

Since the Filter function is a template function it could be used with other types too. In the next example we'll see how to filter words that have at least 4 letters from a sequence of words.

#include < iostream >
#include < vector >
#include < algorithm >
#include < functional >
#include < string >

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
   std::vector< string > snums;
   snums.push_back("one");
   snums.push_back("two");
   snums.push_back("three");
   snums.push_back("four");
   snums.push_back("five");

   // filter the words, notice the new lambda
   std::vector< string > bigwords = Filter< string >(snums, [](string w) {return w.length() > 3;});

   // print the selected words
   for_each(bigwords.begin(), bigwords.end(), [](string s){std::cout << s << std::endl;});

   return 0;
}
three
four
five

Let's consider a second example, a Find (template) function, that takes a sequence and a predicate (that checks a condition for an element), and returns the first element in the sequence for which the predicate returned true.

template < class T >
T Find(const std::vector< T >& sequence,
       std::tr1::function< bool (T) > predicate)
{
   for(auto it = sequence.begin(); it != sequence.end(); ++it)
      if(predicate(*it))
         return *it;

   throw std::runtime_error("Item not found");
}

We'll use this function to find the first element in a sequence that is greater than a given value.

int _tmain(int argc, _TCHAR* argv[])
{
   std::vector< int > nums;
   nums.push_back(1);
   nums.push_back(3);
   nums.push_back(5);
   nums.push_back(7);
   nums.push_back(9);

   int min;
   cout << "find first after: ";
   cin >> min;

   try
   {
      int val = Find< int >(odds, [min](int i){return i > min;});
      cout << val << endl;
   }
   catch(std::runtime_error& ex)
   {
      cout << ex.what() << endl;
   }

   return 0;
}

If you input 4 for instance, it will return 5. If you input 10, an exception will be thrown. You can see that this time the lambda function is [min](int i){return i > min;}. This means that it captures by value the min variable from the local scope, so that it can compare each element with that given value.

The last example I'm going to show is an accumulator function (also known as aggregate or fold). This function takes a sequence of elements, a seed (or initial value) and a function that specifies how to aggregate the elements, and returns the aggregate.

template < class TSource, class TAccumulate >
TAccumulate Aggregate(const std::vector< TSource >& sequence,
                      TAccumulate seed,
                      std::tr1::function< TAccumulate (TSource, TAccumulate) > func)
{
   TAccumulate acc = seed;
   for(auto it = sequence.begin(); it != sequence.end(); ++it)
      acc = func(acc, *it);

   return acc;
}

First, we can use it to compute the sum of all elements in a sequence.

int _tmain(int argc, _TCHAR* argv[])
{
   std::vector< int > nums;
   for(int i = 1; i <= 10; ++i)
      nums.push_back(i);

   int sum = Aggregate< int, int >(nums, 0, [](int e, int acc) {return e + acc;});
   cout << "sum = " << sum << endl;

   int prod = Aggregate< int, int >(nums, 1, [](int e, int acc) {return e * acc;});
   cout << "prod = " << prod << endl;

   return 0;
}
sum = 55
prod = 3628800

The first lambda function above sums the current element with the previous sum, which initially is given as 0. The result is 55. The second lambda function multiplies the current element with the previous product, which initially is 1. The result is 3628800.

But the Aggregate function can be used with other types too. Here is a last example with strings.

int _tmain(int argc, _TCHAR* argv[])
{
   std::vector< string > words;
   words.push_back("the");
   words.push_back("quick");
   words.push_back("brown");
   words.push_back("fox");
   words.push_back("jumps");
   words.push_back("over");
   words.push_back("the");
   words.push_back("lazy");
   words.push_back("dog");

   string sentence = Aggregate< string, string >(
      words,
      "",
      [](string workingSentence, string next){return next + " " + workingSentence;});

   cout << sentence << endl;

   return 0;
}
dog lazy the over jumps fox brown quick the

These were several examples of how lambda functions help us write more generic, and less verbose code. I suggest you read more about lambdas in C++ here.

, , , , , , , , Hits for this post: 14820 .

The new C++0x standard provides support for type inference. The auto keyword that was doing nothing in C++ was given a new meaning: a placeholder for a type inferred by the compiler. For those familiar with C#’s var keyword, this is basically the same.

Here is a comparison between auto in C++ and var in C#.

C++ C#
int _tmain(int argc, _TCHAR* argv[])
{
   auto i = 42;
   auto s = "Hello world!";
   auto d = 12.50;
   auto l = [](int n) {return n * n;};

   cout << "i = " << i << endl;
   cout << "s = " << s << endl;
   cout << "d = " << d << endl;
   cout << "l(i) = " << l(i) << endl;

   return 0;
}
class Program
{
   static void Main(string[] args)
   {
      var i = 42;
      var s = "hello world";
      var d = 12.50;
      //var l = (n) => n * n; // error CS0815

      Console.WriteLine("i = {0}", i);
      Console.WriteLine("s = {0}", s);
      Console.WriteLine("d = {0}", d);
   }
}

with the output

C++ C#
i = 42
s = Hello world!
d = 12.5
l(i) = 1764
i = 42
s = Hello world!
d = 12.5

As you can see, the use is very similar, except that in C++ you can use auto for a lambda expression, while in C# you cannot. The C# compiler raises an error, CS0815, when you try to use var with an anonymous function expressions, a lambda expression, a method group expressions, or the null literal expression, because these don't have a type.

Just like with var, you can only use auto locally, and not as return type from a function, or parameter to a function.

auto power(int n)
{
   return n * n;
}
error C3532: 'auto (int)': incorrect usage of 'auto'
int power(auto n)
{
   return n * n;
}
error C3533: 'auto': a parameter cannot have a type that contains 'auto'

While it might not be of that much help for ints or strings, using auto for instead of vector< int >::iterator, or map< string, list< string >>::const_iterator comes in handy. The auto type inference helps us write less verbose code.

vector< string > words;
words.push_back("hello");
words.push_back("world");

for(auto it = words.begin(); it != words.end(); ++it)
   cout << *it << endl;

In addition to auto, the C++0x standard introduces a new keyword, called decltype (think of it as 'typeof'), that can be used to determine the type of an expression at compile time.

auto i = 42;
decltype(i) i2 = 44;

However, this new keyword was not yet introduced in Visual Studio 2010 CTP. According to Stephan T. Lavavej from the VC++ team, it might be possible to be introduced in a next CTP or beta version.

Implementing auto doesn't implement decltype (C++0x's name for what was previously called typeof) for free.

Paraphrasing our libraries and compiler front-end program manager Damien Watkins, the CTP is only the first look at our VC10 functionality and there is definitely more to come. We understand that certain features "go together", and that adding complementary features is a good thing in general. As always, customer feedback is a vital resource in forming our plans.

, , , , , , , , Hits for this post: 20549 .