#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
enum Op{ADD, SUB, MUL, DIV, MATMUL};
template <typename dtype>
using AlgoFunction = double(*)(const vector<dtype> &, Op);
// for example, the sum function doesn't require template.
// just write sum(a), not sum<float>(a)
template <typename dtype>
double sum(vector<dtype> inputs) {
dtype summer = inputs[0];
for (int i=1; i<inputs.size(); i++) summer = summer + inputs[i];
return double(summer);
}
// i need to do ask this question because I perform the same
// algorithm (linearAlgo, ...) on different types of data
// (dtype = float, double, matrix<float>, matrix<double>, ...
template <typename dtype>
inline dtype numOperate(const dtype &a, const dtype &b, Op op) {
if (op==ADD) return a + b;
if (op==SUB) return a - b;
if (op==MUL) return a * b;
if (op==DIV) return a / b;
}
template <typename dtype>
double linearAlgo(const vector<dtype> &inputs, Op op) {
dtype summer = inputs[0];
for (int i=1; i<inputs.size(); i++) summer = numOperate(summer, inputs[i], op);
return double(summer);
}
template <typename dtype>
double reverseLinearAlgo(const vector<dtype> &inputs, Op op) {
int n = inputs.size();
dtype summer = inputs[n-1];
for (int i=n-2; i>=0; i--) summer = numOperate(summer, inputs[i], op);
return double(summer);
}
template<typename dtype>
vector<double> run(vector<dtype> inputs, Op op, double (*func)(const vector<dtype>&, Op)) {
vector<double> res;
res.push_back(func(inputs, op));
return res;
}
int main()
{
vector<float> a;
vector<double> b;
a.push_back(1); a.push_back(2); a.push_back(3);
b.push_back(1); b.push_back(2); b.push_back(3);
vector<double> res = run(a, ADD, linearAlgo); // allowed without specifying template
vector<double> resf = run(b, ADD, linearAlgo); // still work with multiple data type
// I want to do this assignment without specifying the template.
// in the above linear, linearAlgo (no specifying template) is possible, why not here ?
AlgoFunction<float> functor = reverseLinearAlgo; // works, but I don't want it
//AlgoFunction functor = reverseLinearAlgo; // I want to do this. compile error
vector<double> res2 = run(a, ADD, functor);
cout << res[0] << "\n";
cout << res2[0];
return 0;
}
So I have a function template pointer
template <typename dtype>
using AlgoFunction = double(*)(const vector<dtype> &, Op);
that points to functions like this
template <typename dtype>
double linearAlgo(const vector<dtype> &inputs, Op op) {
dtype summer = inputs[0];
for (int i=1; i<inputs.size(); i++) summer = numOperate(summer, inputs[i], op);
return double(summer);
}
I know that using a template function pointer without specifying template is possible. For example:
vector<float> a;
a.push_back(1); a.push_back(2); a.push_back(3);
vector<double> res = run(a, ADD, linearAlgo); // allowed without specifying template
But then if I declare a variable of type AlgoFunction
, the compiler force me to specify the template.
//AlgoFunction<float> functor = reverseLinearAlgo; // works, but I don't want it
AlgoFunction functor = reverseLinearAlgo; // I want to do this. compile error
This is not good because I have many types of data dtype
, and I don't want to specify the template again for each one.
So how can I declare AlgoFunction functor;
instead of AlgoFunction<some_datatype_name> functor;
?
Thank you.
Edit: the goal is to have a vector<AlgoFunction> functors
instead of vector<AlgoFunction<data_type> >
. Since in the example, res
, and resf
both can be calculated without specifying the template for the 3rd parameter, I want to know if vector<AlgoFunction>
is possible or not.
You cannot. I suspect the confusion stems from missing the difference between a "function" and a "function template".
To explain why your first example worked, first let's examine what is actually happening when you do run(a, ADD, linearAlgo);
. As a reminder, we have:
template <typename dtype>
using AlgoFunction = double(*)(const std::vector<dtype>&, Op);
template <typename dtype>
std::vector<double> run(const std::vector<dtype>&, Op,
double(*)(const std::vector<dtype>&, Op));
Equivalently, we could have had the following:
std::vector<double> run(const std::vector<dtype>&, Op, AlgoFunction<dtype>);
since AlgoFunction
is just an alias.
Now, when we do this:
std::vector<double> a;
run(a, ADD, linearAlgo);
we know that the first argument to run
, std::vector<dtype>
, is std::vector<double>
, and hence dtype
is double
. We can't determine anything about dtype
from the third argument since linearAlgo
is just a template, a "pattern".
Since we know that dtype
must be double
, we can choose and instantiate linearAlgo<dtype>
– that is, linearAlgo<double>
– as our function since that fits our signature, and everything is OK.
Now, what does that have to do with this?
AlgoFunction functor = reverseLinearAlgo;
In this case, we're trying to create a variable. reverseLinearAlgo
is just a function template, not an actual function, and we don't have any other context to determine what type functor
actually is. Hence the compiler error.
Moreover, what would this actually mean? Would functor
have a different type depending on where you used it? If I did auto x = functor;
, what type would x
have? If I did something like
AlgoFunction functor = reverseLinearAlgo;
if (test) {
std::vector<float> x;
functor(x, ADD);
} else {
std::vector<double> x;
functor(x, ADD);
}
would that mean that functor
has dynamic type? This isn't something that works with C++'s (static) type system, and it can quickly get out of hand if this was made legal. This is the case with your wish for std::vector<AlgoFunction>
: you have to store a concrete type. Otherwise the program will need to dynamically instantiate a function based on runtime information: template parameters must be known at compile time.
One possible alternative, if you know the types ahead of time, is to use a std::variant
of the possible types you might instantiate with. That is, something like
std::vector<std::variant<AlgoFunction<float>, AlgoFunction<double>>>;
if each element of the vector should provide one or the other, or else use
std::vector<std::tuple<AlgoFunction<float>, AlgoFunction<double>>>;
if each element of the vector should be usable with either type.
Whether this is useful, and worth the added complexity, is up to you.