lundi 25 avril 2016

c++: confusion about universal reference

I read this (incredibly well written) article about Universal Reference in C++11 by Scott Meyers.

Now, focus on this part of the article:

template <class... Args>
void emplace_back(Args&&... args); // deduced parameter types ⇒ type deduction;
...                                // && ≡ universal references

So, in contrast with other cases, the ellipses doesn't make the && an rvalue reference, but it's still universal references.

From what I've understood, when we have universal references, we can call the function passing both rvalue and lvalues (wow, so cool!)

Now, I've implemented this function:

template <typename ReturnType, typename... Args>
ReturnType callFunction(MemFunc<ReturnType, Args...> memFunc, Args&& ... args) { ...

So (using the same logic of the previous example), && means universal references.

But if I try to make this call:

typedef vector<double> vecD;
vecD vec;
mem.callFunction<vecD, vecD>(sortFunc, vec);

The compiler is going to complain with You cannot bind an lvalue to an rvalue reference

Why this happens?

THE WHOLE CODE:

template <typename ReturnType,typename... Args>
struct MemFunc {
    MemFunc(function<ReturnType(Args...)> func, string name) : func(func), name(name) {}
    function<ReturnType(Args...)> func;
    string name;
};

template <typename ReturnType, typename... Args>
function<ReturnType(Args...)> memoize(function<ReturnType(Args...)> func)
{
    return ([=](Args... args) mutable {
        static map<tuple<Args...>, ReturnType> cache;
        tuple<Args...> t(args...);
        auto result = cache.insert(make_pair(t, ReturnType{}));
        if (result.second) {
            // insertion succeeded so the value wasn't cached already
            result.first->second = func(args...);
        }
        return result.first->second;
    });
}

struct MultiMemoizator
{
    map<string, boost::any> multiCache;
    template <typename ReturnType, typename... Args>
    void addFunction(MemFunc<ReturnType, Args...> memFunc) {
        function < ReturnType(Args...)> cachedFunc = memoize(memFunc.func);
        boost::any anyCachedFunc = cachedFunc;
        auto result = multiCache.insert(pair<string, boost::any>(memFunc.name,anyCachedFunc));
        if (!result.second)
            cout << "ERROR: key " + memFunc.name + " was already inserted" << endl;
    }
    template <typename ReturnType, typename... Args>
    ReturnType callFunction(MemFunc<ReturnType, Args...> memFunc, Args&& ... args) {
        auto it = multiCache.find(memFunc.name);
        if (it == multiCache.end())
            throw KeyNotFound(memFunc.name);
        boost::any anyCachedFunc = it->second;
        function < ReturnType(Args...)> cachedFunc = boost::any_cast<function<ReturnType(Args...)>> (anyCachedFunc);
        return cachedFunc(forward<Args> (args) ...);
    }
    template <typename ReturnType, typename... Args>
    ReturnType callFunction(MemFunc<ReturnType, Args...> memFunc, Args&& ... args) {
        auto it = multiCache.find(memFunc.name);
        if (it == multiCache.end())
            throw KeyNotFound(memFunc.name);
        boost::any anyCachedFunc = it->second;
        function < ReturnType(Args...)> cachedFunc = boost::any_cast<function<ReturnType(Args...)>> (anyCachedFunc);
        return cachedFunc(forward<Args>(args) ...);
    }
};

And this is the main:

typedef vector<double> vecD;

int main()
{
    vecD vec;
    MemFunc<vecD, vecD> sortFunc(sort_vec, "sort");
    MultiMemoizator mem;
    mem.addFunction(sortFunc);
    try
    {
        mem.callFunction<vecD, vecD>(sortFunc, vec);
    }
    catch (boost::bad_any_cast e)
    {
        cout << "Bad function calling: "<<e.what()<<endl;
        return 1;
    }
    catch (KeyNotFound e) 
    {
        cout << e.what()<<endl;
        return 1;
    }
}

Aucun commentaire:

Enregistrer un commentaire