Reputation: 263
I am trying to create a template function which has 5 parameters, four of which should be iterators and the fifth should be a function. What the function should do it should essentially transform each element from the block between first and second iterators passed as arguments and copy them between the third and fourth iterator passed as arguments, using the fifth parameter(function) to transform the elements. The return value is the sum of all elements of the first block.
Here is what I came up with
#include <iostream>
#include <type_traits>
#include <vector>
#include <deque>
using namespace std;
int funk(int a) {
return a*=2;
}
template <typename IterType1, typename IterType2>
auto MyFunction(IterType1 p1,
IterType1 p2,
IterType2 p3,
IterType2 p4,
decltype(p1[0]) CriteriaFunction(decltype(p1[0])))
-> decltype(p1[0])
{
auto temp= remove_reference<decltype (p1[0])>::type;
while(p1 != p2) {
temp+=*p1;
*p3++ = CriteriaFunction(*p1);
p1++;
}
p4=++p3;
return temp;
}
int main()
{
vector<int> v1 = {1,2,3,4,5};
deque<double> d1(5);
auto it1 = v1.begin();
auto it2 = v1.end();
auto it3 = d1.begin();
auto it4 = d1.end();
cout << MyFunction(it1, it2, it3, it4, funk);
return 0;
Now, I am not sure if my template function will actually work the way I wrote it but I will change it up later. The problem is that this will not even compile, the error is:
main.cpp|48|error: invalid user-defined conversion from 'main()::<lambda(int)>' to 'int& (*)(int&)' [-fpermissive]|
Now, I am familiar with passing functions as arguments to other functions, but what if the function the template is receiving depends on the typename arguments of the template. Is my syntax wrong? How can I fix this?
Upvotes: 0
Views: 74
Reputation: 6584
First, what did you mean by that?
auto temp= remove_reference<decltype (p1[0])>::type;
You are assigning a type to a variable.
Next, the actual type of decltype (p1[0])
is a reference to the type stored in the container.
The correct way is to use std::iterator_traits
instead of decltype
and auto
:
typename std::iterator_traits<IterType1>::value_type
So the code could be:
auto MyFunction(
IterType1 p1,
IterType1 p2,
IterType2 p3,
IterType2 p4,
typename std::iterator_traits<IterType1>::value_type
CriteriaFunction(typename std::iterator_traits<IterType1>::value_type))
-> typename std::iterator_traits<IterType1>::value_type {
typename std::iterator_traits<IterType1>::value_type temp = typename std::iterator_traits<IterType1>::value_type();
This would allow you to compile the code. Anyway, from what I can understand from your question, if you want to insert values into the second container, you need to provide an inserter, not just iterators. The code p4=++p3;
doesn't move the end of the second container.
Upvotes: 1
Reputation: 85757
Your syntax is not wrong, but decltype(p1[0])
is int &
, not int
, so your function has the wrong type.
MyFunction
effectively wants an int &(*)(int &)
, but funk
has type int (*)(int)
(it takes and returns a plain int
, not a reference).
You could use remove_reference
, but the easiest fix for this problem is not to bother trying to describe the exact type. Doing so would needlessly limit you anyway: It would prevent lambdas and objects with operator()
from being used.
Why not just do:
template <typename IterType1, typename IterType2, typename FuncType>
auto MyFunction(IterType1 p1, IterType1 p2, IterType2 p3, IterType2 p4, FuncType CriteriaFunction) {
...
}
? Let the compiler worry about the details of FuncType
.
The next two problems are here:
auto temp= remove_reference<decltype (p1[0])>::type;
The compiler assumes (as it always does with dependent names) that remove_reference<decltype (p1[0])>::type
refers to a value, not a type. That's also what's required to make the initialization auto temp = ...
compile. But if you try to instantiate this function template, it will fail because ::type
is a type, not a value.
You can use typename
to tell the compiler that ::type
is in fact a type:
auto temp = typename remove_reference<decltype (p1[0])>::type;
... but that is a syntax error, just like auto i = int;
would be.
However,
auto temp = typename remove_reference<decltype (p1[0])>::type();
compiles and works. Here we're instantiating a default value of type remove_reference<...>::type
.
Upvotes: 1