给定传递给它的参数类型,如何确定函数参数的类型?
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 & 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;
;
这里结合了两个主要问题:
-
如果存在重载,则将
&F::operator()
设为给定类型需要 static_cast
以消除应使用哪个重载的歧义
模板(以及表达它们的任意条件)不能简洁地表达为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 中,用 gcc 替换 decltype
为 typeof
。
我没有办法放弃decltype
。 sizeof
确实提供了未评估的上下文,但在这里似乎没有多大帮助。
Unit Tests Here.
不幸的是,引用似乎存在一个 gcc 错误,float&
减少为float
(以及任何其他引用),错误仍然存在于decltype
所以它只是一个错误的实现:/ Clang 3.0 对 C++11 版本(decltype
)没有问题但我认为没有实现typeof
。
这可以通过要求客户端使用ref<float>
类来解决,然后解包它。只是多了一点负担……
【讨论】:
请注意,其中一个单元测试显然是错误的:takes_float_cref
的测试写得好像它需要 bar const&
。那怎么会过去呢?
@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_function
或std::unary_function
继承并完成它呢?这也不包括重载。
@AzzA 感谢您的建议,但正如 pmr 所指出的,此练习的全部原因是避免此类事情。此外,c++11 弃用了std::unary_function
和std::binary_function
。
@JaredHoberock 哦,好吧,抱歉没用。
@AzzA C++03 中没有n-arguments
。 (好吧,预处理器是极限。)。对于重载:您的基类将如何处理重载 operator()
的类?
@pmr 好的,我已经发布了稍微修改过的版本。我明白,这不是 Jared 想要的,只是为了解决您的顾虑。以上是关于给定传递给它的参数类型,如何确定函数参数的类型?的主要内容,如果未能解决你的问题,请参考以下文章