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.
}
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>()))>
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() );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With