Как определить тип параметра функции с учетом типа передаваемого ему аргумента? - PullRequest
13 голосов
/ 20 января 2012

Мне нужна черта типа, которая будет сообщать тип параметра operator() функтора, учитывая тип функтора и тип передаваемого ему аргумента. По сути, мне нужно точно определить, к какому типу будет преобразован аргумент при передаче его функтору. Для простоты давайте предположим, что меня интересует только (потенциально шаблонная, потенциально перегруженная) operator() с одним аргументом. К сожалению, я ограничен C ++ 03. Это можно сделать? Если нет, то как насчет c ++ 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 .

Ответы [ 3 ]

2 голосов
/ 20 января 2012

Боюсь, что это не совсем возможно без помощи вашего клиента.

TL; DR : модульный тест ошибка (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;
};

Здесь он объединяет две основные проблемы:

  1. Если есть перегрузка, тогда принимается &F::operator() требует static_cast к данному типу для устранения неоднозначности, какую перегрузку следует использовать
  2. Шаблоны (и произвольные условия для их выражения) не могут быть кратко выражены как typedef s

Поэтому клиент (Functor здесь) должен предоставить вам дополнительные хуки, если вы действительно хотите получить этот тип.А без decltype я не вижу, как его получить (заметьте, gcc предоставляет typeof в качестве расширения в C ++ 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;
};

В C ++ 03 замените decltype на typeof с gcc.

Я не вижу возможности отказаться от decltype.sizeof предоставляет недооцененный контекст, но, похоже, здесь не очень помогает.

Модульные тесты здесь .

К сожалению, существует ошибка gcc, с которой, похоже,ссылки и float& уменьшены до float (и любые другие ссылки на самом деле), ошибка остается с decltype, так что это просто ошибочная реализация: / Clang 3.0 не имеет проблем с версией C ++ 11 (decltype), но не реализует typeof Я думаю.

Это можно обойти, потребовав от клиента использовать класс ref<float>, а затем развернув его.Еще немного бремени ...

1 голос
/ 20 января 2012
    #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's 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's not what you are looking for.

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

РЕДАКТИРОВАТЬ: Вот кое-что, может быть, немного ближе к функциональности, которую ищет JAred, но, я понимаю,стиль не привлекает его.Хотя в C ++ 03 я не вижу, как вы можете сделать это по-другому.Обратите внимание, что вы можете TIdentityParameter взять, скажем, 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 );
 }
1 голос
/ 20 января 2012

Для начала я бы сказал следующее:

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) позже.Я думаю, что две вещи ортогональны: определение типа аргумента, а затем принятие решения о том, является ли аргумент, который вы хотите передать, конвертируемым.

Трудно избавиться от не-C ++ 03 decltype.Указание типа функции всегда требует знания аргументов.Как только вы изложите аргументы, все станет спорным.

Та же проблема возникнет с Boost.Function Types.

...