Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deducing template argument for nested template fails

Ok, I read throught quite some “could not deduce template argument” questions but none seems to match my case — or I don't understand the answer…

There's one that I feel goes in the right direction, but I failed extracting the solution for my problem.

The stripped down code in my header looks like this:

template<typename T>
class TemplateProblem
{
public:
    // Do I really need this or did I miss something from the STL?
    template<typename Tin, typename Tout>
    struct UnaryFunction : public std::unary_function<Tin, Tout>
    {
        virtual Tout operator()(Tin input) = 0;
    };

    template<typename Tin, typename Tout>
    struct StaticCast : public UnaryFunction<Tin, Tout>
    {
        virtual Tout operator()(Tin input)
        {
            return static_cast<Tout>(input);
        }
    };

private:
    T * const _data;
    T const _bias;

    template<typename Tin>
    void Init(Tin * data, int length, UnaryFunction<Tin, T> mapper, Tin bias);

public:
    template<typename Tin>
    TemplateProblem(Tin * data, int length, Tin bias = Tin());

    template<typename Tin>
    TemplateProblem(Tin * data, int length, UnaryFunction<Tin, T> mapper, Tin bias = T());
};

template<typename T>
template<typename Tin>
void TemplateProblem<T>::Init(Tin * data, int length, UnaryFunction<Tin, T> mapper, Tin bias)
{
    T mappedBias = mapper(bias);
    for (int i = 0; i < length; i++)
    {
        _data[i] = data[i] + mappedBias;
    }
}

template<typename T>
template<typename Tin>
TemplateProblem<T>::TemplateProblem(Tin * data, int length, UnaryFunction<Tin, T> mapper, Tin bias = T())
    : _data(new T[length]), _bias(bias)
{
    Init(data, length, mapper, bias);
}

template<typename T>
template<typename Tin>
TemplateProblem<T>::TemplateProblem(Tin * data, int length, Tin bias = T())
    : _data(new T[length]), _bias(bias)
{
    StaticCast<Tin, T> cast;
    Init(data, length, cast, bias);
}

I instantiate it like this:

unsigned char pixels[] = {23, 42, 65, 97};
TemplateProblem<int> tp(pixels, 4);

From VS2012 I get these messages:

Error   1   error C2784: 'void TemplateProblem<T>::Init(Tin *,int,TemplateProblem<T>::UnaryFunction<Tin,T>,Tin)' : could not deduce template argument for 'TemplateProblem<T>::UnaryFunction<Tin,T>' from 'TemplateProblem<T>::StaticCast<Tin,Tout>'    ...\templateproblem.h   62  1   TemplateProblem
Error   2   error C2893: Failed to specialize function template 'void TemplateProblem<T>::Init(Tin *,int,TemplateProblem<T>::UnaryFunction<Tin,T>,Tin)' ...\templateproblem.h   62  1   TemplateProblem

The error also occurs when I move the two structs out of the class as this answer suggests.

like image 726
primfaktor Avatar asked Oct 05 '22 14:10

primfaktor


1 Answers

The compiler error is not very helpful in indicating the actual problem.

The actual problem is that you pass UnaryFunction<Tin, T> by value to your Init function (and one of the constructors), but all instantiations of UnaryFunction<> result in an abstract class (which can not be passed by value). The easy solution is to use pass-by-reference for UnaryFunction, so that mapper refers to the actual object passed in.

The typical solution in the STL for passing functors is to use a separate template argument, like this:

template<typename T>
template<typename Tin, Tmapper>
void TemplateProblem<T>::Init(Tin * data, int length, Tmapper mapper, Tin bias)
{
    T mappedBias = mapper(bias);
    for (int i = 0; i < length; i++)
    {
        _data[i] = data[i] + mappedBias;
    }
}

Then you don't need the UnaryFunctionL<> base class. If an incompatible mapper is passed, this will be diagnosed when it is used in the body of the function.

like image 92
Bart van Ingen Schenau Avatar answered Oct 10 '22 03:10

Bart van Ingen Schenau