On Thu, 16 Oct 2014 19:27:52 +0100
Chris Vine <chris@cvine--nospam--.
freeserve.co.uk> wrote:
> On Thu, 16 Oct 2014 12:03:06 -0500
> Robert Hutchings <
rm.hut...@gmail.com> wrote:
[snip]
> > It turns out that this code came from Codeproject.com...
> >
> >
http://www.codeproject.com/Articles/828776/Generic-Memoization-Infrastructure
>
> Interesting. On that point the code is broken but it is easy to leave
> off a reference attribute by mistake.
>
> Actually, the effect of missing off the reference attribute from the
> arguments of Memoize::Func() is worse here than I first thought. The
> effect of applying std::forward to a template type representing a
> function argument mistakenly taken by value is to cast std::forward's
> argument to a rvalue reference. This means that the arguments passed
> to T::Func (for a case where the result of applying the function has
> not previously been memoized) might be invalid, because those
> arguments would have previously been passed via std::forward to
> make_tuple(), which applies std::decay to its arguments, so
> constructing the tuple values from those arguments using (where
> available) the value type's move constructor.
No, I misspoke, it's worse than that. Even if you correct the function
signature, the code is still broken if you pass Memoize::Func() a
rvalue. The basic point is that you should never apply std::forward
in the body of a function to a value more than once. It may be invalid
after the first application.
So there are two significant errors in the code.
Chris