简体   繁体   中英

Why does template argument deduction/substitution fail here?

I am trying to write a simple template that I can use for memoization with functions taking a single argument:

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

But i get the error:

error: no matching function for call to 'memoization(double)'

note: candidate is:

note: template OUT memoization(IN)

note: template argument deduction/substitution failed:

Why does this fail to compile?

Actually I dont understand why template argument deduction/substitution is taking place at all when I specify all the template parameters.

I am using gcc version 4.7.2 (no C++11 enabled)

PS: the template has many more errors than I first realized, but I leave it as is...

Your function template takes three type arguments:

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

You're passing in test for F . test isn't a type, it's a value. Also, the expression F(in) in your function template is wrong for the same reason.


This approach in general is pretty flawed as it seems pretty backwards from what actually is going on. Namely, it's the function that's being memoized, not a value. Also requiring the function value at compile time is pretty limiting.

A better approach is to treat memoization as a decorator. That is:

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

such that:

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

I leave the implementation of Memoized<T> as an exercise.

Why does template argument deduction/substitution fail here?

a. Because there are 3 template arguments and only one actual argument, so two of them are undeduce-able (is that a word?).

b. There is a syntax error. Template argument F is a type, not a callable object.

If this has to work in a pre-c++11 environment, boost 's result_of can help:

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

The answer already got a satisfactory answers, however I was curious if I could get it running with my approach (and with purely pre C++11). Actually it is possible to pass a function pointer as template parameter, one just has to specify this on the template parameter instead of letting it expect a type parameter:

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

output:

not found
1
1
1

not found
1
1
1

Still not sure if this is a good approach, but it seems to work.

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