Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Variadic Templates and aliases?

39 views
Skip to first unread message

Robert Hutchings

unread,
Oct 16, 2014, 9:47:49 AM10/16/14
to
I was given this code yesterday:


#ifndef _MEMOIZE_H_
#define _MEMOIZE_H_

#include <tuple>
#include <map>

template < typename T > struct Memoize
{
template <typename... Args> static auto Func(Args... args)
-> decltype(T::Func(std::forward<Args>(args)...))
{
using INPUT = std::tuple<Args...>;

using RESULT = decltype(T::Func(std::forward<Args>(args)...));

static std::map< INPUT, RESULT > repository;

auto key = std::make_tuple(std::forward<Args>(args)...);

auto it = repository.find(key);

if (it != repository.end())
{
return it->second;
}

else
{
auto result = T::Func(std::forward<Args>(args)...);

repository[key] = result;

return result;
}
}
};

#endif // _MEMOIZE_H_


Now, can someone point me to a good tutorial or book on variadic
templates and the "using SOMETHING = " semantics. Is this just an alias?

Chris Vine

unread,
Oct 16, 2014, 12:30:50 PM10/16/14
to
I usually begin with cpp.reference.com for this kind of thing. This
is a good reference on 'using' for defining types:
http://en.cppreference.com/w/cpp/language/type_alias

This might help with variadic templates, although it is a reference
rather than a tutorial and so assumes some knowledge:
http://en.cppreference.com/w/cpp/language/parameter_pack

using RESULT = decltype(T::Func(std::forward<Args>(args)...));

is the same as

typedef decltype(T::Func(std::forward<Args>(args)...)) RESULT;

This is defective:
template <typename... Args> static auto Func(Args... args)
-> decltype(T::Func(std::forward<Args>(args)...))

If forwarding of rvalues as well as lvalues is to be achieved the
signature should be:

template <typename... Args> static auto Func(Args&&... args)
-> decltype(T::Func(std::forward<Args>(args)...))

Chris

Robert Hutchings

unread,
Oct 16, 2014, 12:39:36 PM10/16/14
to
OK, thanks for the tips. I will check out cppreference.com.

Chris Vine

unread,
Oct 16, 2014, 12:42:11 PM10/16/14
to
On Thu, 16 Oct 2014 17:30:25 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> This is defective:
> template <typename... Args> static auto Func(Args... args)
> -> decltype(T::Func(std::forward<Args>(args)...))
>
> If forwarding of rvalues as well as lvalues is to be achieved the

To be more explicit, the version of Func you posted doesn't forward at
all - it just takes arguments by value (which means that
std::forward<Args> deduces to a lvalue reference in every case).

Chris

Robert Hutchings

unread,
Oct 16, 2014, 1:03:17 PM10/16/14
to
It turns out that this code came from Codeproject.com...

http://www.codeproject.com/Articles/828776/Generic-Memoization-Infrastructure

Chris Vine

unread,
Oct 16, 2014, 2:28:25 PM10/16/14
to
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.

Chris

Chris Vine

unread,
Oct 16, 2014, 2:43:55 PM10/16/14
to
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

Robert Hutchings

unread,
Oct 16, 2014, 2:48:52 PM10/16/14
to
OK. I am reading one of Bjarne's books now, getting to the variadic
templates section. This is more complicated than I thought, although I
knew it wasn't going to be easy :)

0 new messages