C++模板元编程:模板类型上的编译时条件运算符

C++ Template MetaProgramming: Compile Time Conditional Operator on Template Type

本文关键字:编译 条件运算符 编程 C++ 类型      更新时间:2023-10-16

我正在使用模板元编程来创建Variant和Functor(一个通用函子)数据类型。我有一个有趣的问题,需要以特定的方式处理特定参数类型的参数。理想情况下,如果条件满足,我希望使用某种编译时条件运算符来处理方法a的给定参数,如果条件失败,则使用方法B。

高级问题摘要

  • 我需要通过以下方式将变量传递给函数指针上的调用变量的内部值,或者根据所需的参数类型是否为variant类型将变量本身传递给调用

详细信息

调用Functor时,会使用Variant数组来模拟函数参数。下面是我的Functor:的一个重载构造函数的例子

Variant operator()( Variant arg0, Variant arg1, Variant arg2 );

Variant可以用我传递给它的任何类型的数据来构造。在我得到这段代码之前,这一切都很好(这是一个特定的函数调用助手类,用于需要3个参数的签名):

template <typename R, typename T0, typename T1, typename T2>
Variant StaticFnCall3( MultiFnPtr fn, Variant& arg0, Variant& arg1, Variant& arg2 )
{
return reinterpret_cast<typename VoidToType<R>::type(*)(T0, T1, T2)>(fn.StaticFn)( arg0.GetValue<T0>( ), arg1.GetValue<T1>( ), arg2.GetValue<T2>( ) );
}

每个函数存储一个函数指针,函数指针存储在一个名为MultiFnPtr(多函数指针)的并集中。调用Functor时,会将并集类型转换为适当的签名类型,如上所示。传递给Functor的每个Variant都会通过GetValue方法转换为Variant中的值。这意味着我将在调用期间传递给Functor的每个Variant的内部数据转换为它们各自的值。要转换为的值的类型是通过将模板化的StaticFnCall与MultiFnPtr的签名相匹配来推导的。

以下是GetValue:的实现

template <typename TYPE>
const TYPE& VariantBase::GetValue( void ) const
{
return *reinterpret_cast<TYPE *>(data);
}

问题是,我正试图将函数签名封装在一个Functor中,该Functor将Variant作为其参数类型之一。只要Functor被调用时Variant被传递给接受Variant的参数,这就可以了。然而,我需要将一个任意类型传递给采用Variant的参数。然后,当我希望使用Variant的构造函数创建一个Variant以传递给Functor中调用的函数指针时,GetValue将用于将任意类型转换为Variant*,这将导致该任意类型的数据被字面解释为Variant。

我一直在尝试想出一种方法,将值直接传递给StaticFnCall的函数指针,而不是在对应的模板类型为Variant时使用GetValue。我查找了std::enable_if和sfinae,但很难找到一个解决方案。以下是我试图实现的伪代码示例:

template <typename R, typename T0, typename T1, typename T2>
Variant StaticFnCall3( MultiFnPtr fn, Variant& arg0, Variant& arg1, Variant& arg2 )
{
return reinterpret_cast<typename VoidToType<R>::type(*)(T0, T1, T2)>(fn.StaticFn)( (IF_IS_VARIANT) ? arg0 : arg0.GetValue<T0>( ), (IF_IS_VARIANT) ? arg1 : arg1.GetValue<T1>( ), (IF_IS_VARIANT) ? arg2 : arg2.GetValue<T2>( ) );
}

编辑

因此,我发现我可以使用模板化的全局函数,并使用模板专用化以两种方式之一处理参数。然而,这不是一个编译时解决方案,因为全局函数会导致分支,除非函数是内联的。

template<typename T>
const T ArgHandle( const RefVariant& arg )
{
return arg.GetValue<T>( );
}
template<>
const Variant ArgHandle<Variant>( const RefVariant& arg )
{
return Variant( arg );
}

由于函数ArgHandle在编译时具有重载解析,我想可能有某种方法可以在没有函数调用的情况下实现我想要的行为。用途:

#define ARG( NUM ) 
ArgHandle<T##NUM>( arg##NUM )
template <typename R, typename T0, typename T1, typename T2>
Variant StaticFnCall3( MultiFnPtr fn, RefVariant& arg0, RefVariant& arg1, RefVariant& arg2 )
{
return reinterpret_cast<typename VoidToType<R>::type(*)(T0, T1, T2)>(fn.StaticFn)( ARG( 0 ), ARG( 1 ), ARG( 2 ) ) );
}

我不明白你为什么不在问题的这一部分结束后停下来:

template <typename TYPE>
const TYPE& VariantBase::GetValue( void ) const
{
return *reinterpret_cast<TYPE *>(data);
}

并为Variant:添加模板专用化

template <>
const VariantBase& VariantBase::GetValue<VariantBase>( void ) const
{
return *this;
}

就这样算了。有什么不管用的吗?在你的问题后面,你似乎在讨论这个解决方案,但那时你已经引入了毫无意义的ArgHandle函数、宏和辅助函数,结果一团糟。

就我个人而言,我会完全去掉GetValue函数,只提供隐式类型转换运算符,这样您就可以编写fn(arg0, arg1, arg2)了。但我想这取决于代码的其余部分是什么样子的。