1

I have this code:

...
#include "boost/tuple/tuple_comparison.hpp"
...
template <typename ReturnType, typename... Args>
function<ReturnType(Args...)> memoize(const Args && ... args)
{
    using noRef = boost::tuple<typename std::remove_reference<Args>::type...>;
    static map<noRef, ReturnType, less<>> cache;
    auto key = std::tie(noRef{ boost::make_tuple(args ...) });
    auto it = cache.lower_bound(key);
    ReturnType result;
    if (it->first == key) { ...

But when I try to compile it I receive this error:

error C2678: binary '==': no operator found which takes a left-hand operand of type 'const noRef' (or there is no acceptable conversion)

Why this happens, since noRef is an alias for boost::tuple and tuple_comparison should manage this case?

ERROR FOUND, DON'T KNOW HOW TO SOLVE IT:

It seems that the error was in the std::tie operation. So rewriting it as:

    auto key = noRef{ boost::make_tuple(args ...) };

Works fine. The problem is that this solution is inefficient, since key is a potentially expensive copy of the whole tuple, while using tie is a tuple of references (much smaller). So, how can I take a reference to it->first tuple? Should I use the same tie trick?

justHelloWorld
  • 6,478
  • 8
  • 58
  • 138
  • What are the types you instantiate this with? `operator==` for tuples only works if all of the element types support `operator==`. – Benjamin Lindley May 03 '16 at 07:17
  • What are you trying to do here : `std::tie(noRef{ boost::make_tuple(args ...) });`?. Also, if you can use `std::tie`, then why not use `std::tuple` as well? – Nawaz May 03 '16 at 07:21
  • 1
    Also, what does `const` do in `const Args &&`? – Nawaz May 03 '16 at 07:22
  • Edited just a second before of @Nawaz 's answer :D Look at the new section – justHelloWorld May 03 '16 at 07:23
  • i don't think that the `const` is relevant for this question, just from copy and paste of the whole code :) – justHelloWorld May 03 '16 at 07:24
  • `std::tie(noRef{ boost::make_tuple(args ...) })` this makes zero sense. If you insist on using `boost::tuple`, then that should just be `boost::tie(args...)`. None of the `noRef` or `make_tuple` nonsense. – T.C. May 03 '16 at 07:47
  • This absolutely make 100% sense for the applications that I'm implementing, since I don't want to store lvalue references values inside the `boost::tuple` that I'm creating (I have to save it and read it later on a file). – justHelloWorld May 03 '16 at 07:49
  • What I mean is that maybe you are right saying that instruction doesn't make sense, but `boost:tie(args...)` is not feasible for the application – justHelloWorld May 03 '16 at 07:50
  • The map stores tuple of values; the tuple of references should *only* be used for lookup. If you are using it elsewhere, well, that's your problem. – T.C. May 03 '16 at 07:53
  • I'm sorry, I don't want to be rude, but the posted error persists even using `auto key = std::tie(args ...);` and then `if( it->first == key)` – justHelloWorld May 03 '16 at 07:58
  • In particular `error C2446: '==': no conversion from 'std::tuple' to 'const noRef'` is posted below – justHelloWorld May 03 '16 at 08:00
  • If you use `boost::tuple`s, then use `boost::tie`. If you use `std::tuple`s, then use `std::tie`. They are not designed to interop. /sigh – T.C. May 03 '16 at 08:10
  • You're right. If you post this solution as an answer I'm gonna chose it. One last note: is correct that we should use `if(it!=cache.end() && it->first == key)` instead of `if(it->first==key)` and all the rest the same, right? – justHelloWorld May 03 '16 at 08:20
  • 1
    @justHelloWorld Right - you spotted an actual bug in my code. Thanks! – T.C. May 03 '16 at 08:47
  • Np, thanks for your answer (waiting that you officially post it ;) ) – justHelloWorld May 03 '16 at 08:51

1 Answers1

2

The only reason this line compiles at all is MSVC's Evil ExtensionTM which allows non-const lvalue references to bind to temporaries:

auto key = std::tie(noRef{ boost::make_tuple(args ...) });

This should simply be

auto key = boost::tie(args...);

which creates a boost::tuple of references to be used for lookup later.

Also, as noted in the comments, the if check should verify it != cache.end() first before attempting to dereference it (thanks!).

Finally, const Args && ... doesn't make much sense, as one is unlikely to want to accept const rvalues. It probably should be either const Args&... or Args&&....

T.C.
  • 133,968
  • 17
  • 288
  • 421