【问题标题】:Trouble with member function pointer as template argument成员函数指针作为模板参数的问题
【发布时间】:2015-10-11 18:52:52
【问题描述】:

因为F 类型不符合特化,下面代码中被注释掉的行无法编译。谁能解释一下原因?

#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';
}

我在这里遗漏了什么吗?如何获得F 满足专业化要求?我尝试从图片中删除 const 限定符,但同样的问题仍然存在。

我还想保持使用成员函数指针作为模板参数的设计,即使使用非成员函数指针可以解决这个特定问题。

【问题讨论】:

  • 常数。成员函数指针模板参数不是 const 限定的。另外,我认为R Class::*F(Args...) 不是指向成员函数的指针。
  • 我尝试调整常量,包括使calculate() 非常量,但仍然遇到同样的问题。如何解决这个问题?
  • 我认为R Class::*F(Args...) 是一个返回指向成员的指针的函数,即auto F(Args...) -&gt; R Class::*。 OTOH,R (Class::*F)(Args...) 是一个指向成员函数的指针,可以是 const 限定的。
  • 你的记忆似乎还是有问题,不过:coliru.stacked-crooked.com/a/a8c47b31b8a0c3a0
  • 我不太明白你的代码的某些部分,我试图以一种对我来说有意义的方式来修复它:coliru.stacked-crooked.com/a/03f07a4f126cea06希望有帮助。

标签: c++ templates c++11 variadic


【解决方案1】:

感谢 dyp 纠正了我在语法方面的可怕挣扎,我已经完成了我打算做的事情:

#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';  
}

【讨论】:

    【解决方案2】:

    你遇到的最大问题是你试图记住一个签名函数:

    unsigned long calculate();
    

    但是您正在使用num - 1num - 2 调用记忆缓存的返回函数F::get(this)

    换句话说:记忆化依赖于“用相同参数调用的函数产生相同的返回值”这一事实,但你正在记忆的函数不接受任何参数(这本身没有错)但是你应该'也不给它传递任何参数。

    您当前的 FibonacciCalculator 类不能使用 memoization,因为它当前已实现。

    我已经做了一个可能如你所愿的实现;它计算斐波那契数它记忆成员函数调用...... 注意:memoizeMemberFunction() 中有额外的“噪音”,但输出表明它正在工作 - 您会在输出中看到函数调用被使用了两次并且只计算了一次。

    #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';
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-15
      • 2017-10-20
      相关资源
      最近更新 更多