tagloul
tagloul

Reputation: 3

Using a template with transform

I have a problem using the transform function of the algorithm library in c++. I want to use it with a unary template function which means that my transform function would need 3 iterators and the function as arguments. However, my program crashes and the compiler tells me I'm missing an argument which makes no sense since my function is unary and not a binary operation.

My code is the following :

template <typename T>
T reciprocal ( T value )
{ return (T)1/value ; }

int main()
{
    vector<int> vec(5, 2);
    transform(vec.begin(), vec.end(), vec.begin(), reciprocal);
}

Is the use of templates with transform forbidden?

Upvotes: 0

Views: 1575

Answers (3)

Jerry Coffin
Jerry Coffin

Reputation: 490088

Right now, you're depending on the compiler to deduce the correct type over which to instantiate reciprocal, but since you haven't specified the parameter type, it can't do that.

You can get around that by specifying the correct type, as @jaunchopanza has already pointed out. By structuring the code a bit differently:

struct reciprocal {
    template <typename T>
    T operator()(T value)
    { 
        return (T) 1 / value; 
    }
};

...you can get the compiler to deduce the type, so code like this:

std::transform(vec.begin(), vec.end(), vec.begin(), reciprocal());

...works correctly (though note the parens to create an instance of reciprocal).

Of course, a reciprocal on ints doesn't usually make much sense -- all it can ever produce is 0 or 1 (or undefined behavior for an input of 0). You probably want to use a floating point type to get meaningful results:

std::vector<double> vec(5, 2);
std::transform(vec.begin(), vec.end(), vec.begin(), reciprocal());

Upvotes: 0

juanchopanza
juanchopanza

Reputation: 227380

reciprocal is a function template, and std::transform cannot figure out which particular specialization to use. You need to be explicit and give it a template parameter:

transform(vec.begin(), vec.end(), vec.begin(), reciprocal<int>);

Note that in your case, you will be performing integer division 1/2, which will give you a bunch of zeros. To illustrate a floating point reciprocal calculation, and the fact that the template parameter of reciprocal need not be the same as the one used for vec, you can try this:

std::vector<double> vec2;
std::transform(vec.begin(), 
               vec.end(), 
               std::back_inserter(vec2), 
               reciprocal<double>);

This will result in vec2 containing five 0.5 values.

Upvotes: 2

but my program crashes and the compiler tells me im missing an argument which makes no sense since my function is unary and not a binary operation

You should clearly state whether your program compiles and then crashes, not compiles, the compiler crashes and what type of argument is missing.

Now guessing what you mean, the problem is that the transform template does not place any restrictions on the last argument, so the compiler is not able to determine which of the overloads (specialization) of reciprocal should be passed to the transform template. You can specify which one you want manually:

transform(vec.begin(), vec.end(), vec.begin(), &reciprocal<int>);

or may the use of templates with transform be forbidden?

This is an interesting point on itself in that it shows some common misunderstaning. A template is a blueprint from which other elements are created. In this case you have a function template which is just a blueprint from which the compiler will generate different functions by replacing the template arguments. In any context where a function is needed, a function template cannot be used, although a specialization (i.e. a function generated from that function template) can be used.

This becomes slightly confusing in some contexts where you can use the name of the template to refer to a concrete specialization. Namely, inside a class template definition (or the definition of it's members), the name of the template can be used to refer to the specialization. In the case of function templates, the name of the template can be used to refer to all possible specializations of the function template in those contexts where the compiler will be able to discard all but one of those specializations, for example:

int call(int (*ptr)(int)) { return ptr(5); }
call(reciprocal);       // [1]

In [1], reciprocal refers to all possible specializations of the function template, but this particular use is allowed as only one of those specialiations (namely reciprocal<int>) can be used as an argument to call.

But those are the exceptions, the main point is that a class template or function template is not a class or function but a generator from which classes and functions can be created by the compiler.

Upvotes: 1

Related Questions