Welcome to MSDN Blogs Sign in | Join | Help

Stupid Lambda Tricks

Hi.  I’m Arjun Bijanki, the test lead for the compiler front-end and Intellisense engine.  One afternoon a few months ago, I was sitting in my office in building 41 thinking about test passes, when an animated discussion between a couple of colleagues spilled into the hallway and grabbed my attention.  My recollection is that Boris Jabes, whom some of you might have seen deliver the “10 is the new 6” talk at PDC last week, was trying to convince the other colleague that you could write an automatic memoization function for C++0x lambdas.

I became intrigued.

For those that haven’t used C++0x lambdas before, the feature provides a way to define unnamed function objects.  VCBlogger STL wrote up a great post that describes lambdas and their syntax in more detail.

Interestingly, lambdas can be recursive.  For example, here’s a lambda that implements the fibonacci series using recursion:

#include<iostream>

#include<functional>

using namespace std;

using namespace tr1;

 

int main()

{

// implement fib using tr1::function

      function<int(int)> fib1 = [&fib1](int n) -> int

      {

            if(n <= 2)

                  return 1;

            else

                  return fib1(n-1) + fib1(n-2);

      };

 

      cout<<fib1(6);

}

Note that we had to actually name the lambda in order to implement the recursion.  Without a name, how would you make the recursive call?  This self-referencing places some restrictions on how a recursive lambda can be used; the lambda doesn’t refer to itself, it refers to fib1, which happens to be itself.  The restriction is subtle, and shown by the following code:

       function<int(int)> fib1_copy = fib1; // copy fib1

      fib1 = [](int n) { return -1; };    // fib1 now does something else

      cout<<fib1_copy(6);                 // uh oh, doesn't do what we expect

 

Quiz: what would fib1_copy(6) return?

 

Anyway, fib1 isn’t a particularly efficient implementation of the algorithm, but, even though I don’t come from a functional programming background, I find the recursive solution has a certain elegance.  By changing this function to cache values it has already computed, we can make it faster:

function<int(int)> fib2 = [&fib2](int n) -> int

{

      static map<int,int> cache;

      if(n <= 2)

            return 1;

      else if(cache.find(n) == cache.end())

      {

            cache[n] = fib2(n-1) + fib2(n-2);

      }

      return cache[n];

};

(At this point, I should make the caveat that we’re really getting into parlor trick territory.  A functor class is a far better bet for production code, and can do everything here, and more.)

Now the function is really hard to read, and I may as well just implement it iteratively.  This is where the automatic memoization function comes in.  What we’d really like to write is something nice and clean like fib1, but allowing us to memoize it for the faster computation of fib2:

      // memoize the fib1 function and find fib(6)

      memoize(fib1)(6);

 

The three of us tried for an hour or so to write the memoize()function, but intercepting fib1’s recursion to insert a cache proved difficult.

 

// what goes into the highlighted calls? 

// we want to use a cache, not call fib directly.

      return fib1(n-1) + fib1(n-2);

 

If we had a function to independently manage the cache, we make the recursive call this way:

      return check_cache(fib1,n-1) + check_cache(fib1,n-2);

 

This is much cleaner than fib2, but even so, I had to intentionally write fib1 to make use of the cache.  It would be a nicer if I didn’t have to do that.  Eventually, we figured out that we needed a way to hook the recursion and insert our own adapter that checks the cache before making the recursive call.  That way, we can write fib1 normally, and under the covers check the cache. tr1::function doesn’t seem to support this, so after a couple of tries, I coded up adaptable_function and memoize_adapter (Warning: Templates Ahead).

 

template<class Arg>

struct adaptable_function

{

      tr1::function<Arg(Arg)> func;

 

      typedef tr1::function<Arg(tr1::function<Arg(Arg)>,Arg)> adapter_type;

      adapter_type adapter;

 

      // binds a function

      adaptable_function(tr1::function<Arg(Arg)> const& f) : func(f)

      {

      }

 

      // invokes the bound function through an adapter, if one exists

      Arg operator()(Arg p) const

      {

            if(adapter)

                  return adapter(func, p);

            else

                  return func(p);

      }

 

      void set_adapter(adapter_type const& a)

      {

            adapter = a;

      }

      void clear_adapter()

      {

            adapter = adapter_type(); // better way to clear a tr1::function?

      }

 

private:

      //relies on self-referential recursion, so the class is non-copyable

      adaptable_function(adaptable_function const&);

 

};

 

template<class Arg>

struct memoize_adapter

{

      map<Arg,Arg> cache;

 

      Arg operator()(adaptable_function<Arg> func, Arg arg)

      {

            if(cache.find(arg) == cache.end())

            {

                  cache[arg] = func(arg);

            }

            return cache[arg];

      };

};

 

template<class Arg>

adaptable_function<Arg>& memoize(adaptable_function<Arg>& f)

{

      f.set_adapter(memoize_adapter<Arg>());

      return f;

};

 

Now, I can implement fib:

·         Using the elegant recursive algorithm

·         Using a cache without having to build a cache into the function

Here’s the calling code:

int main()

{

      adaptable_function<int> fib = [&] (int n) -> int {

            cout<<"fib("<<n<<")"<<endl; // to show calls

            if(n <= 2)

                  return 1;

            else

                  return fib(n-1) + fib(n-2);

      };

 

      cout<<"normal result = "<<fib(6)<<endl<<endl;

 

      fib.set_adapter(memoize_adapter<int>());

      cout<<"memoized result = "<<fib(6)<<endl<<endl;

 

      fib.clear_adapter();

      cout<<"normal result = "<<fib(6)<<endl<<endl;

 

      cout<<"memoized result = "<<memoize(fib)(6)<<endl<<endl;

}

 

This does the trick!

There are several things I don’t really like about this code.  First and foremost is that memoizing fib  changes its state.  memoize(fib) doesn’t just return a memoized version of fib, it changes fib’s recursive call.  I think this is a limitation of recursive lambdas, since they have to be named.  Or do they…?

Can anyone make this more elegant in C++?  (Note that Bart De Smet recently wrote on this topic from a C# perspective.  It’s a great post, and worth the read!)

-          Arjun

Published Tuesday, November 18, 2008 11:14 AM by vcblog

Comments

Tuesday, November 18, 2008 2:27 PM by Stupid Lambda Tricks | MS Tech News

# Stupid Lambda Tricks | MS Tech News

Tuesday, November 18, 2008 8:40 PM by Stephan T. Lavavej [MSFT]

# re: Stupid Lambda Tricks

[Arjun]

> adapter = adapter_type(); // better way to clear a tr1::function?

Yes: adapter = 0; or adapter = NULL;

This is terser and slightly more efficient.

Tuesday, November 18, 2008 8:51 PM by Stephan T. Lavavej [MSFT]

# re: Stupid Lambda Tricks

[Arjun]

> (At this point, I should make the caveat that we’re really getting into parlor trick territory.

> A functor class is a far better bet for production code, and can do everything here, and more.)

I can demonstrate how to use named functors here.

Memoizing an individual functor is easy:

C:\Temp>type meow.cpp

#include <iostream>

#include <map>

#include <memory>

#include <ostream>

using namespace std;

using namespace std::tr1;

class fib {

private:

   typedef map<int, int> map_t;

   typedef map_t::const_iterator map_ci_t;

   shared_ptr<map_t> m_map;

public:

   fib() : m_map(new map_t) { }

   int operator()(const int n) {

       const map_ci_t i = m_map->find(n);

       if (i == m_map->end()) {

           cout << "fib()(" << n << ")" << endl;

           const int ret = n < 2 ? n : (*this)(n - 1) + (*this)(n - 2);

           (*m_map)[n] = ret;

           return ret;

       } else {

           return i->second;

       }

   }

};

int main() {

   cout << "Result: " << fib()(6) << endl;

}

C:\Temp>cl /EHsc /nologo /W4 meow.cpp

meow.cpp

C:\Temp>meow

fib()(6)

fib()(5)

fib()(4)

fib()(3)

fib()(2)

fib()(1)

fib()(0)

Result: 8

Note that functors should be efficiently copyable, because they're usually passed by value, which is why fib stores shared_ptr<map_t>.

Tuesday, November 18, 2008 9:01 PM by Stephan T. Lavavej [MSFT]

# re: Stupid Lambda Tricks

If you're memoizing lots of functors, you may want to lift out the memoization into a separate class. Here's one way to do it:

C:\Temp>type purr.cpp

#include <exception>

#include <iostream>

#include <map>

#include <memory>

#include <ostream>

#include <string>

#include <utility>

using namespace std;

using namespace std::tr1;

template <typename StatelessFunctor, typename Result, typename Arg1> class memoizer {

private:

   typedef map<Arg1, Result> map_t;

   typedef typename map_t::const_iterator map_ci_t;

   shared_ptr<map_t> m_map;

public:

   memoizer() : m_map(new map_t) { }

   Result operator()(const Arg1& arg1) {

       const map_ci_t i = m_map->find(arg1);

       if (i == m_map->end()) {

           const Result& ret = StatelessFunctor()(*this, arg1);

           m_map->insert(make_pair(arg1, ret));

           return ret;

       } else {

           return i->second;

       }

   }

};

struct fib {

   int operator()(memoizer<fib, int, int>& mem, const int n) const {

       cout << "fib()(" << n << ")" << endl;

       return n < 2 ? n : mem(n - 1) + mem(n - 2);

   }

};

struct squarefree {

   string operator()(memoizer<squarefree, string, int>& mem, const int n) const {

       cout << "squarefree()(" << n << ")" << endl;

       if (n == 0) {

           return "0";

       } else {

           const string prev = mem(n - 1);

           string ret;

           for (string::const_iterator i = prev.begin(); i != prev.end(); ++i) {

               switch (*i) {

                   case '0':

                       ret += "12";

                       break;

                   case '1':

                       ret += "102";

                       break;

                   case '2':

                       ret += "0";

                       break;

                   default:

                       cout << "EPIC FAIL" << endl;

                       terminate();

               }

           }

           return ret;

       }

   }

};

int main() {

   memoizer<fib, int, int> mem_fib;

   cout << "mem_fib(6): " << mem_fib(6) << endl << endl;

   cout << "mem_fib(8): " << mem_fib(8) << endl << endl;

   memoizer<squarefree, string, int> mem_sqf;

   cout << "mem_sqf(3): " << mem_sqf(3) << endl << endl;

   cout << "mem_sqf(5): " << mem_sqf(5) << endl << endl;

}

Tuesday, November 18, 2008 9:11 PM by Stephan T. Lavavej [MSFT]

# re: Stupid Lambda Tricks

Output:

C:\Temp>cl /EHsc /nologo /W4 purr.cpp

purr.cpp

C:\Temp>purr

fib()(6)

fib()(5)

fib()(4)

fib()(3)

fib()(2)

fib()(1)

fib()(0)

mem_fib(6): 8

fib()(8)

fib()(7)

mem_fib(8): 21

squarefree()(3)

squarefree()(2)

squarefree()(1)

squarefree()(0)

mem_sqf(3): 10212012

squarefree()(5)

squarefree()(4)

mem_sqf(5): 10212010201210212012102010212012

Tuesday, November 18, 2008 9:12 PM by Stephan T. Lavavej [MSFT]

# re: Stupid Lambda Tricks

This way, fib and squarefree can focus on their computations, leaving all of the caching work to memoizer.

Note that I've generalized this to handle arbitrary result and argument types, but it requires stateless unary functors. Generalizing this further to handle stateful functors of arbitrary arity is an exercise left to the reader (a stateful functor would need to be stored within the memoizer, and the memoizer's map would need to be from a tuple of argument types to the result type).

Also note that I use insert() instead of op[]() because op[]() default-constructs the value if it's not present, and in general result types don't have to have default constructors.

Tuesday, November 18, 2008 10:04 PM by Sohail

# re: Stupid Lambda Tricks

This is the best I could do in C++03. Fun exercise, thanks!

#include <boost/function.hpp>

#include <map>

#include <iostream>

template<typename Signature>

struct memoized;

template<typename R,typename T1>

struct memoized<R(T1)>

{

 typedef std::map<T1,R> cache_t;

 typedef boost::function<R(memoized<R(T1)> &,T1)> memo_function_t;

 explicit

 memoized(memo_function_t const & f):

   m_f(f)

 {}

 R operator()(T1 t1)

 {

   typename cache_t::iterator it = m_cache.find(t1);

   if(it == m_cache.end())

   {

     std::cout << t1 << " not in cache." << std::endl;

     return m_cache[t1] = m_f(*this,t1);

   }

   else

   {

     std::cout << t1 << " in cache." << std::endl;

     return it->second;

   }

 }

private:

 memo_function_t m_f;

 cache_t         m_cache;

};

template<typename R, typename T1>

memoized<R(T1)>

memoize(R(*f)(memoized<R(T1)> &,T1))

{

 return memoized<R(T1)>(f);

}

int main()

{

 struct myfib

 {

   static std::size_t calc(memoized<std::size_t(std::size_t)> & memo_fib,

                           std::size_t n)

   {

     if(n <= 2) return 1;

     else return memo_fib(n-1) + memo_fib(n-2);

   }

 };

 boost::function<std::size_t(std::size_t)> fib =

   memoize(myfib::calc);

 std::cout << fib(10) << std::endl;

 std::cout << fib(10) << std::endl;

}

Tuesday, November 18, 2008 11:40 PM by Craig T. Nelson

# re: Stupid Lambda Tricks

Is it not possible to define a Y-combinator for C++ lambdas?  The Y-combinator is how you get recursive anonymous functions in many other languages.

http://en.wikipedia.org/wiki/Fixed_point_combinator

Wednesday, November 19, 2008 3:49 AM by Andre

# re: Stupid Lambda Tricks

Shouldn't

fib1 = [](int n) { return -1; };

be

fib1 = [](int n) -> int { return -1; };

?

Why does the first one even compile? Is the return value by default int?

Wednesday, November 19, 2008 4:06 AM by Andre

# re: Stupid Lambda Tricks

Also why does

fib1 = [](int n) -> double { return -1; };

compile? So function<> isn't type safe?

Wednesday, November 19, 2008 6:18 AM by bruteforce

# re: Stupid Lambda Tricks

Interesting acrobatics, but I am a KISS fan.

I prefer not to mandate a C++ black belt (with several Dans on occassion) on coworkers who try to understand and modify my code, so thanks but I'll pass.

Is there anything in the above code that cannot be done in plain C in a way that 90% of the dev population can understand and 80% can modify/extend without a mistake?

Why do architects feel so compelled to save the world by providing infrastructure and plumbing for everything conceivable under the sun?

What about memoization? If I am in such a corner case where caching the results of a function call will *actually* improve performance, what makes you think I would opt for an obscure and totally incomprehensible generic template that I cannot understand or debug, rather than a custom-tailored, totally non-reusable, top-performing, totally understandable and debugable solution?

Don't get me wrong, I am not an anti-STL, do-it-yourself (CMyHashTable, CMyDynamicArray, CMyOS) gangho. I am just a KISS fan (including the rock band). If something can be done in a way that is simpler, easier to understand, debug and extend, then I prefer the simpler way.

I just get so frustrated when people do all this acrobatic stuff in production code just because (a) they can do it (b) it's cool to do it, without thinking back a lil'bit or actually having mastered the 'tools' they are using.

A similar example is 'patternitis'. I have seen countless C++ freshmen reading the GangOf4 Design Patterns book and then creating a total mess in everything, like deciding to implement the Visitor pattern on a problem that required Composite and ended up coding a third pattern alltogether from the same book, still naming the classes CVisitorXYZ (probably they opened the book on the wrong page at some point).

I have met exactly 1 guy (I called him the "Professor") who knew C++ well enough and had the knowledge to apply the patterns where they ought to be applied. His code was a masterpiece, it worked like a breeze, but when he left, noone else in the house could figure things out.

So what's the point with these Lambda stuff really? Increase the expression of the language? Are we doing poetry or software? Why should we turn simple code that everyone understands into more and more elegant and concise code that only few can understand and make it work?

I have been coding in C (drivers) and C++ for 15 years and not once was I trapped because I was missing lambda expressions or similar syntactic gizmos.

So what's the point really? Please enlighten me. I don't say that *I* am right and *YOU* are wrong. I am saying that I don't see, I don't understand the positive value that these things bring in that far outweighs the problems they cause by complicating the language.

I am all ears :-)

Warm Regards,

Dimitris Staikos

Wednesday, November 19, 2008 7:41 AM by Mike Diack

# re: Stupid Lambda Tricks

I'm with Dimitris Staikos / bruteforce on this.

Fix the core of the IDE and compiler first. Then if you have time add these features. I don't hear people screaming for them. On the other hand, I do hear people screaming about poor IDE performance, documentation issues and poor code analysis/warnings.

Mike

Wednesday, November 19, 2008 9:39 AM by Anthony Williams

# re: Stupid Lambda Tricks

Well, first off there's no need for this std::function<> malarky --- just use auto:

auto fib = [&](int n) -> int{

   return (n<=2)?1:(fib(n-1)+fib(n-2));

};

Secondly, with auto and decltype we can write a memoize adapter for any callable function by using decltype to get the return type of the function call. [Warning: untested code follows]

template<typename T>

struct memoized

{

   T func;

   explicit memoized(T func_):

       func(func_)

   {}

   struct adaptable

   {

       struct base

       {

           virtual ~base()

           {}

           virtual const std::type_info& type() const=0;

           virtual bool is_less(base const& rhs) const=0;

           virtual bool is_equal(base const& rhs) const=0;

       };

       template<typename U>

       struct value:

           base

       {

           U data;

           value(U data_):

               data(data_)

           {}

           virtual const std::type_info& type() const

           {

               return typeid(U);

           }

           virtual bool is_less(base const& rhs) const

           {

               return type().before(rhs.type()) ||

                   ((type()==rhs.type()) &&

                    (data<static_cast<value const&>(rhs).data));

           }

           virtual bool is_equal(base const& rhs) const

           {

               return (type()==rhs.type()) &&

                   (data==static_cast<value const&>(rhs).data);

           }

        };

       std::auto_ptr<base> data;

       template<typename U>

       explicit adaptable(U data_):

           data(new value<U>(data_))

       {}

       template<typename U>

       U& extract() const

       {

           if(typeid(U)==data->type())

           {

               return static_cast<value<U>*>(data.get())->data;

           }

           else

           {

               throw "wrong type";

           }

       }

       bool operator<(adaptable const& rhs) const

       {

           return data->is_less(rhs.data);

       }

       bool operator==(adaptable const& rhs) const

       {

           return data->is_equal(rhs.data);

       }

   };

   typedef std::map<adaptable,adaptable> map_type;

   std::shared_ptr<map_type> memo;

   memoized(T func_):

       func(func_),

       memo(new map_type)

   {}

   template<typename U>

   auto operator()(U arg) -> decltype(func(arg))

   {

       typedef decltype(func(arg)) result_type;

       adaptable adapted_arg(arg);

       map_type::iterator existing_entry=memo->find(adapted_arg);

       if(existing_entry==memo->end())

       {

           result_type res=func(arg);

           memo->insert(map_type::value_type(adapted_arg,adaptable(res)));

           return res;

       }

       else

       {

           return existing_entry->second.cast<result_type>();

       }

   }

};

template<typename T>

memoized<T> memoize(T func)

{

   return memoized<T>(func);

}

int main()

{

   auto f1 = [&f1] (int n) -> int {

       std::cout<<"f1("<<n<<")"<<std::endl;

       if(n<=2)

       {

           return 1;

       }

       else

       {

           return f1(n-1) + f1(n-2);

       }

   };

   f1(10);

   auto f2 = memoize([&f2] (int n) -> int {

           std::cout<<"f2("<<n<<")"<<std::endl;

           if(n<=2)

           {

               return 1;

           }

           else

           {

               return f2(n-1) + f2(n-2);

           }

       });

   f2(10);

}

Wednesday, November 19, 2008 9:41 AM by Anthony Williams

# re: Stupid Lambda Tricks

@Andre:

In

fib1 = [](int n) { return -1; };

the body of the lambda is a single return statement, so return type is the type of the expression in the return statement.

Wednesday, November 19, 2008 9:46 AM by pingpong

# re: Stupid Lambda Tricks

Lambdas took the place previously reserved for generic programming :)

What's Andrei Alexandrescu doing these days?

Wednesday, November 19, 2008 10:17 AM by Andre

# re: Stupid Lambda Tricks

"so return type is the type of the expression in the return statement."

I hope there is a compiler option "force explicit lambda return type" which is on by default. Or at least a W4 warning for implicit return types.

Wednesday, November 19, 2008 10:33 AM by Andre

# re: Stupid Lambda Tricks

Why does

function<int(int)> fib1 = [fib1](int n) -> int

crash if I capture fib1 by value?

Wednesday, November 19, 2008 11:22 AM by swigger

# re: Stupid Lambda Tricks

hi, how do i ask you a question? do you have an email address?

Wednesday, November 19, 2008 12:08 PM by Anthony Williams

# re: Stupid Lambda Tricks

@Andre

function<int(int)> fib1 = [fib1](int n) -> int

crashes because fib1 is not yet constructed when the lambda is being built, so you're copying a not-yet-constructed value, which is undefined behaviour => crash.

Wednesday, November 19, 2008 1:09 PM by Arjun Bijanki [MSFT]

# re: Stupid Lambda Tricks

@Stephan:

Thanks for the named functor examples.  I note you had to write the functors in a special way to allow for memoization.  In practice, certainly a good idea.  But can you think of a way to write this without needing to allow for memoization up front?

@Andre:

>> Also why does

>>

>> fib1 = [](int n) -> double { return -1; };

>>

>> compile? So function<> isn't type safe?

Well, this is really the same issue as:

double foo() { return -1; }

tr1::function<> is indeed type safe, but converting the return expression to the return type will invoke implicit conversions.

@Dimitris and Mike,

I agree with you.  The code I wrote is needlessly complex, and I would be horrified if anyone introduced it into production code!  However, this parlor trick aside, I personally find there are real world cases where lambdas make code clearer than named functors (STL's lambdas post, which I link to, contains some good examples).

Mike, have you read the posts about the Intellisense improvements we're working on?

http://blogs.msdn.com/vcblog/archive/2008/02/29/intellisense-part-2-the-future.aspx

@Craig

A Y-combinator example would be neat...I was trying to come up with one a few weeks ago, but I ran into some problems expressing some of the types, and had to resort to boost::any (kind of an ugly hack).  Specifically, I needed a function that took a reference to its own type as a parameter.  Let me know if you are able to build one!

@swigger

Yep, my email address is arjun dot bijanki at microsoft.com.

Wednesday, November 19, 2008 3:22 PM by Mike Diack

# re: Stupid Lambda Tricks and a wasted opportuinty to improve VS 10

Yes I have read about Intellisense. While this  is certainly welcome, there are MANY MANY igssues as well as the lags due to intellisense that people including me are upset about.

Examples as before:

1) Poor compilation times.

2) Help integration for C++ is not as good as it was with 6. Now you often get help on c# not C++

3) Why are new projects STILL created at warning level 3 by default.

I could go on and on.

4) Poor level of ongoing support. You made 6 SP's available for VC++6, and one for each of 2003, 2005 and 2008. This doesn't represent much ongoing support. Instead customers are blindly expected to pay for the next version in the often vain hope that problems get resolved.

I am happy to talk more in depth if you wish to email me.

Remove the spaces:

mike_diack @ hotmail.com

Mike

Wednesday, November 19, 2008 5:29 PM by Raphael

# re: Stupid Lambda Tricks

@Mike Diack,

I think you would find that http://blogs.msdn.com/somasegar/archive/2007/08/08/visual-c-futures.aspx is a very interesting reading.

I believe all of your questions have been asked there.

Wednesday, November 19, 2008 6:26 PM by Mike Diack

# re: Stupid Lambda Tricks

Sorry Raphael. I don't want this to generate into a flame war. I've been reading this blog for months including the posts in August that you've mentioned

I'm less convinced than ever, based on what I'm seeing now that Microsoft are really fulfilling users needs.

Maybe I need to get out more, but not one C++ developer I know of is screaming out for more state of the art C++ language features, but almost without exception they are begging for improvements to their IDE and toolset.

I'm not having huge problems with Intellisense at the moment, I know some people are, and I've seen their problems, but a number of them have only got themselves to blame since against my advice they've not installed the service packs and hotfixes that changed the intellisense priority and reworked the algorithm. Besides which Whole Tomato's tool does a much better job anyway - and I've just begun using that.

But all of the other issues remain and some of them are so fundamental and have been issues for 6+ years, left unresolved.

You want to make reference to the August blog posting?

Have a look at Larry's comments on it.

"Wednesday, August 08, 2007 10:21 PM by Larry  "

These are the real kind of issues I'm alluding to.

Likewise Stephen Kelletts comments and my friend Anna-Jayne Metcalfe's or Brians or Gordons....

I could go on.

Seriously - this shows I'm not the only one who is not all that bothered about bleeding edge C++ standard stuff, but instead wants a productive, efficient toolset.

The recent videos demoing VS10, have shown us that very little notice actually appears to have been taken of these requests - with the possible exception of intellisense.

Mike

Thursday, November 20, 2008 12:09 PM by Andre

# re: Stupid Lambda Tricks

>> Well, this is really the same issue as:

>> double foo() { return -1; }

No, it's not.

int    x() { return 1; }

double y() { return 1; }

int _tmain(int argc, _TCHAR* argv[])

{

   typedef int(*pfn)(void);

   pfn fn = x; // ok

   fn = y; // error

   function<int(void)> func = x; // ok

   func = y; // ok

   return 0;

}

Tuesday, November 25, 2008 8:53 PM by Jonathan

# re: Stupid Lambda Tricks

Thanks for the interesting article. I'd love to see more like this.

Saturday, May 23, 2009 10:12 AM by Van's House

# Recursive Algorithm in C++

Many recursive algorithms have initial parameters. For example, Fibonacci Number is defined as: Fn =

New Comments to this post are disabled
 
Page view tracker