给定传递给它的参数类型,如何确定函数参数的类型?

时间:2012-01-19 23:47:10

标签: c++ templates c++11 parameter-passing

我需要一个类型特征,它会根据仿函数的类型和传递给它的参数类型报告仿函数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;
}

related question(答案并不能满足我的需要)给出了需要这样一个特征的背景。我在ideone上进行了进一步的单元测试。

3 个答案:

答案 0 :(得分:2)

如果没有客户的帮助,我担心这是不可能的。

TL; DR :单元测试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;
};

这里结合了两个主要问题:

  1. 如果存在过载,则取&F::operator()需要static_cast到给定类型以消除应使用哪种重载的歧义
  2. 模板(以及表达它们的任意条件)不能简洁地表达为typedef s
  3. 因此,如果您真的希望获得此类型,客户端(此处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中,使用gcc将decltype替换为typeof

    我没有办法放弃decltypesizeof确实提供了一个未经评估的背景,但在这里似乎没什么帮助。

    Unit Tests Here

    不幸的是,引用中似乎存在gcc错误,并且float&减少到float(以及任何其他引用),错误仍然在decltype,所以它只是一个错误的实现:/ Clang 3.0对C ++ 11版本(decltype)没有问题,但我认为没有实现typeof

    这可以通过要求客户端改为使用ref<float>类,然后解包它来解决。只是更多的负担......

答案 1 :(得分:1)

要开始,我会选择:

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会出现同样的问题。

答案 2 :(得分:1)

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