views:

333

answers:

4

I am writing a functor F which takes function of type void (*func)(T) and func's argument arg.

template<typename T>
  void F(void (*func)(T), WhatTypeHere? arg)
{
  func(arg);
}

Then functor F calls func with arg. I would like F not to copy arg, just to pass it as reference. But then I cannot simply write "void F(void (*func)(T), T&)" because T could be a reference. So I am trying to write a trait, which allows to get proper reference type of T:

T -> T&
T& -> T&
const T -> const T&
const T& -> const T&

I come up with something like this:

template<typename T>
 struct type_op
{
 typedef T& valid_ref_type;
};

template<typename T>
 struct type_op<T&>
{
 typedef typename type_op<T>::valid_ref_type valid_ref_type;
};

template<typename T>
 struct type_op<const T>
{
 typedef const T& valid_ref_type;
};

template<typename T>
 struct type_op<const T&>
{
 typedef const T& valid_ref_type;
};


template<typename T>
  void F(void (*func)(T), typename type_op<T>::valid_ref_type arg)
{
  func(arg);
}

Which doesn't work for example for

void a(int x) { std::cout << x << std::endl; }
F(&a, 7);

Giving error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int’ in passing argument 2 of ‘void F(void (*)(T), typename type_op::valid_ref_type) [with T = int]’

How to get this trait to work?

+2  A: 

All you need is to remove a reference:

template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T&> { typedef T type; };

Then add it again as follows:

remove_reference<T>::type&

Your function should be declared as follows:

template<typename T>
void F( void (*func)(T), const typename remove_reference<T>::type& arg )
{
  func(arg);
}
Kirill V. Lyadvinsky
Same error with your trait: "invalid initialization of non-const reference of type ‘int F(
maciekp
@maciekp, fixed.
Kirill V. Lyadvinsky
Sorry, didn't see the const. Works for me!
maciekp
Will fail for `void f(int } int main() { int n; F(f, n); }`.
Johannes Schaub - litb
+2  A: 

It's a bit vague in my mind, but I think that boost (maybe boost::bind) solves this by only providing const T& traits, and requiring the use of ref(x) to indicate a non-const reference.

Marcelo Cantos
Unfortunately, I am not allowed to use boost. But your answer is certainly correct.
maciekp
+5  A: 
Roger Pate
Absolutely perfect solution. Also fixed call for reference argument: void a(int }
maciekp
+1  A: 

You could also use add_reference from Boost.TypeTraits to achieve the type-mapping you need.

larsm