【问题标题】:C++ Template MetaProgramming: Compile Time Conditional Operator on Template TypeC++ 模板元编程:模板类型上的编译时间条件运算符
【发布时间】:2012-11-03 18:01:24
【问题描述】:

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

高级问题总结

  • 我需要将变体传递给对函数指针的调用 变量的内部值,或者将变量本身传递给调用,具体取决于预期的参数类型是否为 Variant 类型。

详情

当调用 Functor 时,Variants 数组用于模拟函数参数。这是我的 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(多功能指针)的union中。当调用 Functor 时,联合被类型转换为适当的签名类型,如上所示。传递给 Functor 的每个 Variant 都通过 GetValue 方法转换为 Variant 中保存的值。这意味着我将在调用期间传递给 Functor 的每个变体的内部数据转换为它们各自的值。要转换为的值的类型是通过将模板化的 StaticFnCall 与 MultiFnPtr 的签名匹配推导出来的。

这里是GetValue的实现:

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

问题是我试图将一个函数签名包装在一个将 Variant 作为其参数类型之一的 Functor 中。只要在调用 Functor 时将 Variant 传递给采用 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 的函数调用()

标签: c++ templates reflection metaprogramming variant


【解决方案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)。但我想这取决于你的代码的其余部分是什么样的。

【讨论】:

  • 我将尝试模板专业化!谢谢你的主意。一旦我搞砸了,我会回复你的。
猜你喜欢
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多