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

3
我正在使用模板元编程创建Variant和Functor(通用函数对象)数据类型。我遇到了一个有趣的问题,需要针对特定的参数类型以某种方式处理参数。理想情况下,如果条件满足,我希望使用某种编译时条件运算符来使用方法A处理给定的参数,如果条件失败则使用B。
高级问题总结:
我需要根据期望的参数类型是Variant类型还是其他类型,通过将Variant的内部值传递给函数指针调用或将Variant本身传递给调用来将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>( ) );
}

每个Functor都存储了一个函数指针,这个函数指针存储在一个名为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的参数传递任意类型的值。GetValue将被用于将任意类型转换为Variant *,这会导致任意类型的数据被字面上解释为Variant,而我希望使用Variant的构造函数创建Variant以传递给调用Functor内部的函数指针。
我一直在尝试直接向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 ) ) );
}

很可能那个小函数会被内联,你就不用担心对ArgHandle<>()的函数调用了。 - Tocs
1个回答

1
我不明白为什么你不只是在问题的这个部分停下来:

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)了。但我想这取决于你的其余代码是什么样子的。


我会尝试模板特化!谢谢你的想法。一旦我玩弄它,我会回到你这里。 - RandyGaul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接