2010-04-13 3 views
3

Я пишу функтор F, который выполняет функцию типа void (* func) (T) и аргумента func arg.get const или non-const reference type from trait

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

Тогда функтор F вызывает func с arg. Я бы хотел, чтобы F не копировал arg, просто чтобы передать его в качестве ссылки. Но тогда я не могу просто написать «void F (void (* func) (T), T &)», поскольку T может быть ссылкой. Так что я пытаюсь написать черту, которая позволяет получить надлежащую ссылочный тип Т:

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

я придумать что-то вроде этого:

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); 
} 

Который не работает, например, для

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

ошибка Отдает: недействителен инициализация неконстантного ссылки типа «Int &» из временного типа «INT» при прохождении аргумента 2 of 'void F (void (*) (T), typename type_op :: valid_ref_type) [с T = int]'

Как получить эту признак на работу?

+0

Я думаю, что это очень похоже на этот: http://stackoverflow.com/questions/2539361/c-template-functor-cannot-deduce-reference-type –

ответ

5
template<class T> 
struct forwarding { typedef T const& type; }; 
template<class T> 
struct forwarding<T&> { typedef T& type; }; 

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

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

Вашего отображение было близко, вы на самом деле хотите T сопоставляется с Т сопза & тоже:

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

Обратите внимание, что функции, имеющей параметр типа Т сопза есть подпись Т! Сопз деталь реализации:

void f(int const); 
typedef void F(int); // typedef of function type 
F* p = &f; // no error! f's signature doesn't include const 
+0

Абсолютно идеальное решение. Также фиксированный вызов ссылочного аргумента: void a (int & x) {std :: cout << x << std :: endl; } – maciekp

2

Все, что вам нужно удалить ссылку:

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

Затем добавить его снова следующим образом:

remove_reference<T>::type& 

Ваша функция должна быть объявлена ​​следующим образом:

template<typename T> 
void F(void (*func)(T), const typename remove_reference<T>::type& arg) 
{ 
    func(arg); 
} 
+1

Такая же ошибка с вашей чертой: «Недействительная инициализация не- const ссылка типа «int &» из временного типа «int». Также передается const int & генерирует ошибку: const int & cri = 7; F (& a, cri); "Неверная инициализация ссылки типа 'int &' из выражения типа 'const int' ' – maciekp

+0

@maciekp, fixed. –

+1

Извините, не видел const. Работает на меня! – maciekp

1

Это немного расплывчато в моем сознании, но я думаю, что повышение (возможно, boost :: bind) решает это, только предоставляя const T& признак с и требует использования ref(x) для указания неконстантной ссылки.

+0

К сожалению, мне не разрешено использовать boost. Но ваш ответ, конечно, правильный. – maciekp

1

Вы также можете использовать add_reference от Boost.TypeTraits для достижения типа картографии вам нужно.