Reputation: 1226
I'm a Scala/Java programmer looking to reintroduce myself to C++ and learn some of the exciting features in C++0x. I wanted to start by designing my own slightly functional collections library, based on Scala's collections, so that I could get a solid understanding of templates. The problem I'm running into is that the compiler doesn't seem to be able to infer any type information for templated function objects.
FC++ seems to have solved this using "Signatures". These seem really similar to the result_type typename, and I thought I would get this using the new function syntax. Can anyone suggest a way to do this sort of thing in C++0x, if it's possible, or at least explain how FC++ was able to accomplish this? Here's a snippet of code I was playing around with
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<class T>
class ArrayBuffer {
private:
vector<T> array;
public:
ArrayBuffer();
ArrayBuffer(vector<T> a) : array(a) {}
template<typename Fn>
void foreach(Fn fn) {
for(unsigned int i = 0; i < array.size(); i++) fn(array[i]);
}
template<typename Fn>
auto map(Fn fn) -> ArrayBuffer<decltype(fn(T()))> {
vector<decltype(fn(T()))> result(array.size());
for(int unsigned i = 0; i < array.size(); i++) result[i] = fn(array[i]);
return result;
}
};
template<typename T>
class Print {
public:
void operator()(T elem) { cout<<elem<<endl; }
};
template<typename T>
class Square{
public:
auto operator()(T elem) -> T {
return elem * elem;
}
};
int main() {
vector<int> some_list = {5, 3, 1, 2, 4};
ArrayBuffer<int> iterable(some_list);
ArrayBuffer<int> squared = iterable.map(Square<int>()); // works as expected
iterable.foreach(Print<int>()); // Prints 25 9 1 4 16 as expected
iterable.foreach(Print()); // Is there a way or syntax for the compiler to infer that the template must be an int?
ArrayBuffer<int> squared2 = iterable.map(Square()); // Same as above - compiler should be able to infer the template.
}
Upvotes: 5
Views: 1151
Reputation: 59347
You seem to be reinventing the C++ standard library. I'm not talking about your containers.
C++ is already pretty functionally equipped.
I think you're missing a few key points about the C++ standard library.
ArrayBuffer::foreach == std::for_each
ArrayBuffer::map == std::transform
Here's an example of using C++ functionally. It's also a good example of why C# didn't use iterators. Though they are very powerful, their verbosity is intimidating to C#'s target audience. Java doesn't use iterators as they aren't Object Oriented and the language designers were really anal about that in the beginning.
struct Print
{
template<typename T>
void operator()( const T& t )
{ std::cout << t << std::endl; }
};
struct Squared
{
template<typename T>
T operator()( const T& t )
{ return t*t; }
};
int main()
{
std::vector<int> vi;
std::foreach( vi.begin(), vi.end(), Print());
std::foreach( vi.begin(), vi.end(), [](int i){ std::cout<<i<<std::endl; } );
std::vector<int> vi_squared;
std::transform( vi.begin(), vi.end(), std::back_inserter(vi_squared), Squared() );
// or
vi_squared.resize( vi.size() );
std::transform( vi.begin(), vi.end(), vi_squared.begin(), Squared() );
}
Upvotes: 1
Reputation: 59347
Is there a way or syntax for the compiler to infer that the template must be an int?
The problem is the template doesn't need to be an int.
This is equally valid.
iterable.foreach(Print<float>());
What you're really asking is.
Can i make a template functor have a different default depending on the context in which it is used?
The answer is no. I suppose we could theoretically do it for simple cases. However the corner cases quickly make this infeasible. The true context of the function is the complete compilation unit, not just the line where it is instantiated.
Upvotes: 0
Reputation: 507373
You can make the operator()
a template too
class Print {
public:
template<typename T>
void operator()(T elem) { cout<<elem<<endl; }
};
Then you can pass Print()
. For passing arguments like in ArrayBuffer<decltype(fn(T()))>
I recommend using declval
, so you could also work with non-default constructible T
ArrayBuffer<decltype(fn(declval<T>()))>
Upvotes: 5