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

Posted

技术标签:

【中文标题】给定传递给它的参数类型,如何确定函数参数的类型?【英文标题】:How to determine the type of a function parameter given the type of argument passed to it? 【发布时间】:2012-02-14 15:26:28 【问题描述】:

我需要一个类型特征,它会根据函子的类型和传递给它的参数的类型报告函子的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 进行了进一步的单元测试。

【问题讨论】:

你不能简单地在每个仿函数中添加相同的typedef 吗? typedef float const &amp; TParameter; 之类的东西在 takes_float_cref 中,然后只测试 Functor::TParameter @AxxA,谢谢你的建议。是的,这是一种解决方案,但我发现要求客户这样做是很严厉的。我不确定它是否能很好地扩展到 N 参数的一般情况。 嗯,实际上可能没那么糟糕。在这里打字太长了,我会发布它作为答案。看看对你有没有帮助。 @AzzA:主要缺点是接受多种类型参数(模板或重载)的函子无法轻松定义这样的typedef +1 表示有单元测试的问题! 【参考方案1】:

恐怕如果没有您的客户的帮助,这完全不可能。

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

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

    如果存在重载,则将 &amp;F::operator() 设为给定类型需要 static_cast 以消除应使用哪个重载的歧义 模板(以及表达它们的任意条件)不能简洁地表达为typedefs

因此,如果您真的希望获得这种类型,客户端(此处为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 替换 decltypetypeof

我没有办法放弃decltypesizeof 确实提供了未评估的上下文,但在这里似乎没有多大帮助。

Unit Tests Here.

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

这可以通过要求客户端使用ref&lt;float&gt; 类来解决,然后解包它。只是多了一点负担……

【讨论】:

请注意,其中一个单元测试显然是错误的:takes_float_cref 的测试写得好像它需要 bar const&amp;。那怎么会过去呢? @Xeo:确切地说,使用assert 意味着我们永远不会走那么远。我会修补测试。 @Xeo: test patched, Ive go for compile-time failure 它表明gcc 只是忽略了***引用:x @MatthieuM。谢谢!我不确定我是否理解客户在您的第一个示例中给我的提示。 (1.利用返回值)你能解释一下吗? @JaredHoberock:如果您可以强制客户端使其operator() 返回与其参数相同的参数,那么您可以直接使用它。这完全限制了可实现的目标(并且没有通过您的测试用例),因此更容易让他复制其工作并提供“选择”重载。唯一的问题是他可能更难让operator()Select 保持同步......这两种情况都远非完美:x【参考方案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)稍后。我认为这两件事是正交的: 推断参数类型,然后决定是否要使用参数 喜欢传入是可以兑换的。

非 C++03 的 decltype 很难摆脱。指定函数 type 总是需要参数的知识。只要你愿意 把论据拼出来,整个事情就没有实际意义了。

Boost.Function Types 也会出现同样的问题。

【讨论】:

谢谢。我希望我需要传入实际的参数类型来区分将调用哪个重载,以及哪个可能模板化的operator() 将被实例化。 哦,您的问题与重载无关。如果是这样,你已经为失败启航了。重载的函数必须始终通过 static_cast 来消除歧义,这总是需要您拼出类型。所以你还需要传入返回类型。如果没有 C++11,这会很快变得丑陋。 我的错。我在ideone上发布的代码清楚地说明了我感兴趣的所有案例。丑陋的我可以处理,我只是想知道它是否可能:) 据我所知。给定一个重载函数和一个参数,我不知道如何获取将通过模板元编程调用的函数的类型。 @JaredHoberock 看看我在这里的尝试:无论如何,这并不能解决你的问题。 ***.com/questions/8935520/…【参考方案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'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 );
 

【讨论】:

那么,为什么不从std::binary_functionstd::unary_function 继承并完成它呢?这也不包括重载。 @AzzA 感谢您的建议,但正如 pmr 所指出的,此练习的全部原因是避免此类事情。此外,c++11 弃用了std::unary_functionstd::binary_function @JaredHoberock 哦,好吧,抱歉没用。 @AzzA C++03 中没有n-arguments。 (好吧,预处理器是极限。)。对于重载:您的基类将如何处理重载 operator() 的类? @pmr 好的,我已经发布了稍微修改过的版本。我明白,这不是 Jared 想要的,只是为了解决您的顾虑。

以上是关于给定传递给它的参数类型,如何确定函数参数的类型?的主要内容,如果未能解决你的问题,请参考以下文章

模板与泛型编程——模板实参推断

什么是形参和实参?参数传递的方式都有哪些?

Python参数类型

形参和实参

delphi中的函数传参如何传枚举参数?

可变参函数