Как определить тип параметра функции, учитывая тип переданного ему аргумента?
Мне нужна черта типа, которая сообщит тип параметра funter operator()
, заданный тип функтора и тип переданного ему аргумента. В принципе, мне нужно точно определить, к какому типу будет преобразован аргумент, передав его функтору. Для простоты предположим, что меня интересует только (потенциально templated, потенциально перегруженный) operator()
с одним аргументом. К сожалению, я ограничена С++ 03. Это можно сделать? Если нет, как насчет С++ 11?
Вот пример:
#include <cassert>
#include <type_traits>
template<typename Functor, typename Argument>
struct parameter_type
{
// what goes here?
typedef ... type;
};
struct takes_float_cref
{
void operator()(const float &);
};
int main()
{
// when calling takes_float_cref::operator() with an int,
// i'd expect a conversion to const float &
assert(std::is_same(parameter_type<takes_float_cref, int>::type, const float &>::value);
return 0;
}
A связанный вопрос (чей ответ не дает мне совершенно то, что мне нужно) дает контекст необходимости такой черты. Я добавил дополнительные модульные тесты ideone.
Ответы
Ответ 1
Я боюсь, что это не совсем возможно без помощи вашего клиента.
TL; DR: unit test fail (grrr gcc).
Общий случай вашего вопроса - этот функтор:
struct Functor {
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
operator()(T t) const;
void operator(double d) const;
};
Он сочетает в себе два основных вопроса:
- Если есть перегрузка, то для принятия
&F::operator()
требуется static_cast
для данного типа, чтобы устранить, какая перегрузка должна использоваться
- Шаблоны (и произвольные условия для их выражения) не могут быть выражены как
typedef
s
Поэтому клиент (Functor
здесь) должен предоставить вам дополнительные крючки, если вы действительно хотите получить этот тип. И без decltype
я не вижу, как его получить (обратите внимание, gcc
предоставляет typeof
как расширение в С++ 03).
Получение клиентом подсказок:
// 1. Make use of the return value:
struct Functor {
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
operator()(T t) const;
double operator(double d) const;
};
// 2. Double up the work (but leave the return value as is)
struct Functor {
template <typename T>
static typename std::enable_if<std::is_integral<T>::value, T>::type Select(T);
static double Select(T);
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
operator()(T t) const;
void operator(double d) const;
};
Скажем, мы идем во второй случай (оставляя возвращаемое значение свободным для другого использования).
template <typename F, typename T>
struct parameter {
static T t;
typedef decltype(F::Select(t)) type;
};
В С++ 03 замените decltype
на typeof
на gcc.
Я не вижу способа отказаться от decltype
. sizeof
предоставляет неоценимый контекст, но, похоже, он здесь не помогает.
Тестирование устройств здесь.
К сожалению, есть ошибка gcc, которая кажется с ссылками, а float&
уменьшается до float
(и любая другая ссылка действительно), ошибка остается с decltype
, поэтому это просто ошибка:/Clang 3.0 не имеет проблем с версией С++ 11 (decltype
), но не реализует typeof
, я думаю.
Это можно обойти, требуя, чтобы клиент вместо этого использовал класс ref<float>
, а затем развернул его. Просто немного больше бремени...
Ответ 2
Чтобы начать работу, я бы пошел с этим:
template<typename F>
struct parameter_type_impl;
// may be with variadic arguments
template<typename R, typename A, typename F>
struct parameter_type_impl<R (F::*)(A)> {
typedef A type;
};
template<typename F>
struct parameter_type {
typedef typename parameter_type_impl<decltype(&F::operator())>::type type;
};
Я не понимаю, почему вы должны передать фактический тип аргумента. Если
преобразование не может быть выполнено, вы должны использовать специальные меры
(например, SFINAE). Я думаю, что две вещи ортогональны:
выведя тип аргумента, а затем решив, будет ли аргумент
как передать в кабриолет.
Отказ от не-С++ 03 трудно избавиться. Указание функции
тип всегда требует знания аргументов. Как только вы
изложить аргументы, все это будет спорным вопросом.
Та же проблема возникла бы с Boost.Function Types
.
Ответ 3
#include <iostream>
template< typename PParameter00 = void, typename PParameter01 = void, typename PParameter02 = void, typename PParameter03 = void >
struct TIdentityParameter // Users need to inherit from it. Add more types as needed.
{
typedef PParameter00 TType00;
typedef PParameter01 TType01;
typedef PParameter02 TType02;
typedef PParameter03 TType03;
};
struct TUserFunctor00 : public TIdentityParameter< float const &, int, void * >
{
void operator()( float const &, int, void * );
// or they can do
//void operator()( TType00, TType01, TType02 );
};
struct TUserFunctor01 : public TIdentityParameter< char const *, double >
{
void operator()( char const*, double );
// or they can do
//void operator()( TType00, TType01 );
};
template< bool pValue >
struct TValueBool
{
static bool const sValue = pValue;
};
template< typename PType00, typename PType01 >
struct TIsSame : public TValueBool< false >
{
};
template< typename PType >
struct TIsSame< PType, PType > : public TValueBool< true >
{
};
int main( void )
{
std::cout << TIsSame< TUserFunctor00::TType02, void * >::sValue << std::endl;
std::cout << TIsSame< TUserFunctor01::TType00, double >::sValue << std::endl;
return ( 0 );
}
Code on [ideone][1]. I don't think it asking too much from users to inherit from your struct in a pattern explained to them. After all, they want to work with your library. Anyway, maybe it not what you are looking for.
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++
EDIT: вот что-то, может быть, немного ближе к функциональности, которую ищет JAred, но, я понимаю, стиль не нравится ему. Хотя, в С++ 03, я не вижу, как вы можете сделать это по-другому. Обратите внимание: вы можете сделать TIdentityParameter
take, say 16 аргументов шаблона, чтобы охватить 16 возможных типов. Еще раз, да, пользователь должен наследовать и указывать типы. Ideone:
#include <iostream>
struct TOneCrazyStruct
{
};
template< typename PParameter00 = TOneCrazyStruct, typename PParameter01 = TOneCrazyStruct, typename PParameter02 = TOneCrazyStruct,
typename PParameter03 = TOneCrazyStruct, typename PParameter04 = TOneCrazyStruct >
struct TIdentityParameter //Users will need to inherit from this struct as shown below.
{
typedef PParameter00 TType00;
typedef PParameter01 TType01;
typedef PParameter02 TType02;
typedef PParameter03 TType03;
typedef PParameter04 TType04;
};
struct TUserFunctor00 : public TIdentityParameter< float const &, int, void *, double >
{
void operator()( float const &, int, void * );
void operator()( double );
};
template< bool pValue >
struct TValueBool
{
static bool const sValue = pValue;
};
template< typename PType00, typename PType01 >
struct TIsSame : public TValueBool< false >
{
};
template< typename PType >
struct TIsSame< PType, PType > : public TValueBool< true >
{
};
template< typename PFunctor, typename PParameter >
struct THasType : public TValueBool<
TIsSame< typename PFunctor::TType00, PParameter >::sValue || TIsSame< typename PFunctor::TType01, PParameter >::sValue
|| TIsSame< typename PFunctor::TType02, PParameter >::sValue || TIsSame< typename PFunctor::TType03, PParameter >::sValue >
{
};
int main( void )
{
std::cout << THasType< TUserFunctor00, void * >::sValue << std::endl;
std::cout << THasType< TUserFunctor00, long double >::sValue << std::endl;
return ( 0 );
}