For example, at first my deref_and_apply functor stored the m_op as a non-const reference, but then I hit a problem where I couldn't use an unnamed temporary e.g.
So I concluded that it was the wrong thing to do, but I don't understand why....
Yes, you can't bind unnamed temporaries to non-const references. However, it should not be a problem if the functor itself is stored by value (since they should be small and designed to be passed by value).
again, it's the "pointer to reference" error.
Well, the cause of the error seems to be that at template instantiation
Code:
class deref_and_apply : public unary_function<typename Op::argument_type*,
typename Op::result_type>
becomes
Code:
class deref_and_apply : public unary_function<const Node&*, void>
which is illegal.
I'm no master in these quarters but it seems that it would be possible to throw in yet another metaclass that removes the reference'ness from the type through partial specialization. (I didn't test, whether there might be problems with constness as well.)
Try the following. It shouldn't create any unnecessary copies (except the last call copies each object twice - not sure if it is because the argument goes through several wrappers):
Code:
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
// Simple test class
struct Node {
Node(const Node& n) : m_id(n.m_id) { cout << "COPY NODE " << m_id << "\n"; }
Node(int id) : m_id(id) { }
int m_id;
};
ostream& operator<<(ostream& os, const Node& n) { return os << "NODE " << n.m_id << "\n"; }
// Send object of type T to ostream
template <typename T>
class Printer : public unary_function<T, void> {
public:
Printer(ostream& rOS) : m_os(rOS) { }
void operator()(const T& t) { m_os << t; }
protected:
ostream& m_os;
};
template <class T>
struct ref_stripper
{
typedef T type;
};
template <class T>
struct ref_stripper<T&>
{
typedef T type;
};
// functor to dereference argument before applying the unary_function
template<class Op>
class deref_and_apply : public unary_function<typename ref_stripper<typename Op::argument_type>::type*,
typename Op::result_type>
{
public:
deref_and_apply(const Op& op) : m_op(op) { }
typename Op::result_type operator() (const typename ref_stripper<typename Op::argument_type>::type* p)
{
return m_op(*p);
}
protected:
Op m_op;
};
template <class Op>
inline deref_and_apply<Op> derefapply(const Op& op)
{ return deref_and_apply<Op>(op); }
void printNodeRef(const Node& n) { cout << n; }
void printNodeObj(const Node n) { cout << n; }
int main()
{
vector<const Node*> l_list1;
l_list1.push_back(new Node(1));
l_list1.push_back(new Node(2));
vector<Node> l_list2;
l_list2.push_back(Node(10));
l_list2.push_back(Node(20));
for_each(l_list1.begin(), l_list1.end(), derefapply( Printer<Node>(cout) ));
for_each(l_list2.begin(), l_list2.end(), Printer<Node>(cout) );
std::cout << "PrintNodeRef\n";
for_each(l_list1.begin(), l_list1.end(),
derefapply( ptr_fun(printNodeRef) ) );
std::cout << "PrintNodeObj\n";
for_each(l_list1.begin(), l_list1.end(),
derefapply( ptr_fun(printNodeObj) ) );
return 0;
}