简体   繁体   中英

Trouble with member function pointer as template argument

The commented-out line in the code below won't compile because type F does not meet the specialization. Can someone explain why?

#include <memory>
#include <functional>
#include <map>
#include <tuple>

template <typename R, typename T, typename... Args>
std::function<R(Args...)> memoizeMemberFunction (R T::*f(Args...), const T* t) {
    auto cache = std::make_shared<std::map<std::tuple<const T*, Args...>, R>>();
    return ([f, cache](T* t, Args... args) {
        const std::tuple<const T*, Args...> tuple(t, args...);
        if (cache->find(tuple) == cache->end())
            (*cache)[tuple] = (t->*f)(args...);  // Insert 'tuple' as a new key in the map *cache.
        return (*cache)[tuple];
    });
}

template <typename Class, typename Fptr, Fptr> struct MemberFunctionMemoizer;

template <typename Class, typename R, typename... Args, R Class::*F(Args...)>
struct MemberFunctionMemoizer<Class, R (Class::*)(Args...) const, F> {
    static std::function<R(Class*, Args...)>& get (const Class* p) {
        static std::function<R (Args...)> memoizedF (memoizeMemberFunction(F, p)); 
        return memoizedF;
    }
};

struct FibonacciCalculator {
    unsigned long calculate(unsigned num) const {
        using F = MemberFunctionMemoizer<FibonacciCalculator,
            unsigned long (FibonacciCalculator::*)(unsigned) const, &FibonacciCalculator::calculate>;
//      return (num < 2) ? num : F::get(this)(num - 1) + F::get(this)(num - 2);
        // Won't compile because F does not meet the specialization.
    }
};

#include <iostream>

int main() {
    FibonacciCalculator fib;
    std::cout << fib.calculate(10) << '\n';
}

Am I missing something here? How to get F to meet the specialization? I tried removing the const qualifiers out of the picture, but the same problem persists.

I also want to maintain the design of using a member function pointer as template argument, even though there is a solution to this particular problem by using a non-member function pointer.

The biggest issue you have is the fact that you're trying to memoize a function of signature:

unsigned long calculate();

but you're calling the memoization cache's returned function F::get(this) with num - 1 or num - 2 .

In other words: memoization relies on the fact that "function called with identical arguments yields identical return value" but the function you're memoizing doesn't take any parameters (nothing wrong with that in itself) but then you shouldn't pass it any parameters either.

Your current FibonacciCalculator class cannot use memoization, as it is currently implemented.

I have made an implementation that sort of might do what you hoped; it computes Fibonacci numbers and it memoizes the memberfunction call ... Note: there's extra 'noise' in the memoizeMemberFunction() but that's output to show that it's working - you'll see in the output that function calls are used twice and computed only once.

#include <iostream>
#include <memory>
#include <functional>
#include <map>
#include <tuple>

template <typename R, typename T, typename... Args>
std::function<R(Args...)> memoizeMemberFunction (R (T::*f)(Args...), T* t) {
    auto cache = std::make_shared<std::map<std::tuple<T*, Args...>, R>>();
    return [f, t, cache](Args... args) {
        const std::tuple<T*, Args...> tuple(t, args...);
        if (cache->find(tuple) == cache->end()) {
            (*cache)[tuple] = (t->*f)(args...);  // Insert 'tuple' as a new key in the map *cache.
            std::cout << "Computed f(";
            int dummy[sizeof...(Args)] = { (std::cout << args << ", ", 0)... };
            std::cout << ") = " << (*cache)[tuple] << std::endl;
        }
        std::cout << "Using f(";
        int dummy[sizeof...(Args)] = { (std::cout << args << ", ", 0)... };
        std::cout << ") = " << (*cache)[tuple] << std::endl;
        return (*cache)[tuple];
    };
}

struct FibonacciCalculator {
    unsigned long num;
    unsigned long calculate(unsigned long n = (unsigned long)-1) {
        static auto memoizedF (memoizeMemberFunction(&FibonacciCalculator::calculate, this));
        if( n==(unsigned long)-1 )
            return memoizedF(num);
        return (n < 2) ? n : memoizedF(n-1) + memoizedF(n-2);
    }
};

int main() {
    FibonacciCalculator fib{ 10 };
    std::cout << fib.calculate() << '\n';
}

Thanks to dyp for correcting my horrible struggle with syntax, I have accomplished what I set out to do:

#include <memory>
#include <functional>
#include <map>
#include <tuple>

template <typename R, typename T, typename... Args>
std::function<R(Args...)> memoizeMemberFunction (R (T::*f)(Args...), T* t) {
    auto cache = std::make_shared<std::map<std::tuple<T*, Args...>, R>>();
    return ([f, cache, t](Args... args) {
        const std::tuple<T*, Args...> tuple(t, args...);
        if (cache->find(tuple) == cache->end())
            (*cache)[tuple] = (t->*f)(args...);  // Insert 'tuple' as a new key in the map *cache.
        return (*cache)[tuple];
    });
}

template <typename R, typename T, typename... Args>
std::function<R(Args...)> memoizeConstMemberFunction (R (T::*f)(Args...) const, const T* t) {
    auto cache = std::make_shared<std::map<std::tuple<const T*, Args...>, R>>();
    return ([f, cache, t](Args... args) {
        const std::tuple<const T*, Args...> tuple(t, args...);
        if (cache->find(tuple) == cache->end())
            (*cache)[tuple] = (t->*f)(args...);  // Insert 'tuple' as a new key in the map *cache.
        return (*cache)[tuple];
    });
}

template <typename Class, typename Fptr, Fptr> struct MemberFunctionMemoizer;
template <typename Class, typename Fptr, Fptr> struct ConstMemberFunctionMemoizer;

template <typename Class, typename R, typename... Args, R (Class::*F)(Args...) const>
struct ConstMemberFunctionMemoizer<Class, R (Class::*)(Args...) const, F> {
    static std::function<R(Args...)>& get (const Class* p) {
        static std::function<R (Args...)> memoizedF (memoizeConstMemberFunction(F, p)); 
        return memoizedF;
    }
};

template <typename Class, typename R, typename... Args, R (Class::*F)(Args...)>
struct MemberFunctionMemoizer<Class, R (Class::*)(Args...), F> {
    static std::function<R(Args...)>& get (Class* p) {
        static std::function<R (Args...)> memoizedF (memoizeMemberFunction(F, p)); 
        return memoizedF;
    }
};

// Testing
#include <iostream>
#include <vector>

struct FibonacciCalculator {
    std::vector<unsigned long> computed;
    unsigned long calculate (unsigned num) const {
        using F = ConstMemberFunctionMemoizer<FibonacciCalculator, unsigned long (FibonacciCalculator::*)(unsigned) const, &FibonacciCalculator::calculate>;  // 'decltype(&FibonacciCalculator::calculate)' can be used in place of 'unsigned long (FibonacciCalculator::*)(unsigned) const'.
        return (num < 2) ? num : F::get(this)(num - 1) + F::get(this)(num - 2);
    }
    unsigned long calculateAndStore (unsigned num) {
        using F = MemberFunctionMemoizer<FibonacciCalculator, unsigned long (FibonacciCalculator::*)(unsigned), &FibonacciCalculator::calculateAndStore>;  // 'decltype(&FibonacciCalculator::calculateAndStore)' can be used in place of 'unsigned long (FibonacciCalculator::*)(unsigned)'.
        const unsigned long result = (num < 2) ? num : F::get(this)(num - 1) + F::get(this)(num - 2);
        computed.push_back(result);
        return result;
    }
};

int main() {
    FibonacciCalculator fib;
    for (unsigned i = 1; i < 20; i++)
        std::cout << fib.calculate(i) << ' ';  // 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
    std::cout << '\n';

    for (unsigned i = 1; i < 20; i++)
        std::cout << fib.calculateAndStore(i) << ' ';  // 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
    std::cout << '\n';
    for (unsigned long x : fib.computed)
        std::cout << x << ' ';  // 1 1 0 1 1 2 2 3 3 5 5 8 8 13 13 21 21 34 34 55 55 89 89 144 144 233 233 377 377 610 610 987 987 1597 1597 2584 2584 4181
    std::cout << '\n';  
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM