【问题标题】:Why does template argument deduction/substitution fail here?为什么模板参数推导/替换在这里失败?
【发布时间】:2017-01-27 03:36:22
【问题描述】:

我正在尝试编写一个简单的模板,我可以将它用于带有单个参数的函数的记忆:

#include <map>          

template <typename F,typename OUT,typename IN> 
OUT memoization(IN in){
    static std::map<IN,OUT> memo;
    static typename std::map<IN,OUT>::iterator found = memo.find(in);
    if (found != memo.end()) { return found->second; }  
    OUT res = F(in);
    memo(in) = res;
    return res;
}

double test(double x) { return x*x; }

int main(){
    for (int i=0;i<5;i++){
        memoization<test,double,double>(i*0.5);
    }
}

但我得到了错误:

错误:没有匹配的函数调用'memoization(double)'

注意:候选人是:

注意:模板 OUT memoization(IN)

注意:模板参数扣除/替换失败:

为什么编译失败?

实际上我不明白为什么在我指定所有模板参数时会发生模板参数扣除/替换。

我使用的是 gcc 版本 4.7.2(未启用 C++11)

PS:模板的错误比我最初意识到的要多得多,但我保持原样......

【问题讨论】:

  • test 不是类型。 decltype(test) 是。

标签: c++ templates memoization


【解决方案1】:

您的函数模板采用三个 type 参数:

template <typename F,typename OUT,typename IN> 
OUT memoization(IN in) { ... }

您将 test 传递给 Ftest 不是类型,而是值。此外,出于同样的原因,您的函数模板中的表达式 F(in) 是错误的。


一般来说,这种方法存在很大缺陷,因为它看起来与实际情况大相径庭。也就是说,它是被记忆的函数,而不是一个值。在编译时要求函数值也是非常有限的。

更好的方法是将记忆化视为装饰器。那就是:

template <class F>
Memoized<F> memoize(F f) {
    return {f};
}

这样:

auto memo_test = memoize(test);
memo_test(0); // performs computation
memo_test(0); // doesn't perform computation
memo_test(0); // ditto

我将Memoized&lt;T&gt; 的实现留作练习。

【讨论】:

  • UPS。是的,愚蠢的错误。但是,我不确定它是否仍然可以按我的意愿工作。这个想法是,对于每个函数,我都会得到不同的模板实例化,因此得到一个单独的地图
  • 与您提出的解决方案不是我的test 和一些double foo(double) 的相同模板实例化吗?在这种情况下,我的模板方法将毫无用处
  • @tobi303 整个方法从根本上是有缺陷的,更新答案给你一个更好的方向。
  • 不幸的是,他没有可用的 c++11。也许是使用 boost::result_of 的候选人?
【解决方案2】:

为什么模板参数推导/替换在这里失败?

因为有 3 个模板参数和只有一个实际参数,所以其中两个是不可推导的(这是一个词吗?)。

b.存在语法错误。模板参数 F 是一种类型,而不是可调用对象。

如果这必须在 c++11 之前的环境中工作,boostresult_of 可以提供帮助:

#include <map>         
#include <boost/utility/result_of.hpp>

//
// now that template arguments are all used when collecting function
// arguments, the types F and IN can be deduced.
//    
template <typename F,typename IN> 
typename boost::result_of<F(IN)>::type memoization(F f, IN in)
{
  typedef typename boost::result_of<F(IN)>::type OUT;
    static std::map<IN,OUT> memo;
    static typename std::map<IN,OUT>::iterator found = memo.find(in);
    if (found != memo.end()) { return found->second; }  
    OUT res = f(in);
    memo[in] = res;
    return res;
}

double test(double x) { return x*x; }

int main(){
    for (int i=0;i<5;i++){
        memoization(test, i*0.5);
    }
}

【讨论】:

  • 一个。没问题,OP 显式提供了所有模板参数。
  • @Barry 他是。哎哟:(
【解决方案3】:

答案已经得到了令人满意的答案,但是我很好奇我是否可以让它与 pre C++11 一起使用。实际上可以将函数指针作为模板参数传递,只需在模板参数上指定它,而不是让它期望一个类型参数:

#include <iostream>
#include <map>
using namespace std;
 
template <class T, class R, R (*Func)(T)>
R memoized(T in) {
    static std::map<T,R> memo;
    typename std::map<T,R>::iterator found = memo.find(in);
    if (found != memo.end()) { return found->second; }  
    std::cout << "not found" << std::endl;
    R res = Func(in);
    memo[in] = res;
    return res;
}
 
double test(double x){return x*x;}
double test2(double x){return x;}
 
int main() {
    std::cout << memoized<double,double,test>(1) << std::endl;
    std::cout << memoized<double,double,test>(1) << std::endl;
    std::cout << memoized<double,double,test>(1) << std::endl;
    std::cout << std::endl;
    std::cout << memoized<double,double,test2>(1) << std::endl;
    std::cout << memoized<double,double,test2>(1) << std::endl;
    std::cout << memoized<double,double,test2>(1) << std::endl;
 
    return 0;
}

输出:

not found
1
1
1

not found
1
1
1

仍然不确定这是否是一个好方法,但它似乎有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-19
    • 2018-04-28
    • 2014-09-22
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多