How To Convert Templated Function Overloads to Par

2019-06-04 13:14发布

I have several functions that I want to specialize based on type qualities, such as "character, signed-integer, unsigned-integer, floating-point, pointer"; using type_traits seems like the way to do this, and have code similar to the to the following:

#include <tr1/type_traits>
#include <iostream>

template<bool, typename _Tp = void>
struct enable_if 
{ };

template<typename _Tp>
struct enable_if<true, _Tp>
{
    typedef _Tp type;
};


template< typename T >
inline void
foo_impl( typename enable_if< std::tr1::is_integral< T >::value, T >::type const& )
{
    std::cout << "This is the function-overloaded integral implementation.\n";
}

template< typename T >
inline void
foo_impl( typename enable_if< std::tr1::is_floating_point< T >::value, T >::type const& )
{
    std::cout << "This is the function-overloaded floating-point implementation.\n";
}

template< typename T >
inline void
function_overloads_foo( T const& arg )
{
    foo_impl< T >( arg ); // vital to specify the template-type
}

void function_overloads_example()
{
    function_overloads_foo( int() );
    function_overloads_foo( float() );
}

except in my real code, I also have bar,baz, etc., along with foo.

However, I would like to group all of these functions per quality into one templated class as static methods. How is this best done? Here's my naive, and broken attempt to use Tags, SFINAE, and partial-specialization:

struct IntegralTypeTag;
struct FloatingPointTypeTag;

template< typename T, typename U = void >
class Foo
{
};

template< typename T >
class Foo< T, typename enable_if< std::tr1::is_integral< T >::value, IntegralTypeTag >::type >
{
    static void foo( T const& )
    {
        std::cout << "This is the integral partial-specialization class implementation.\n";
    }
};

template< typename T >
class Foo< T, typename enable_if< std::tr1::is_floating_point< T >::value, FloatingPointTypeTag >::type >
{
    static void foo( T const& )
    {
        std::cout << "This is the floating-point partial-specialization class implementation.\n";
    }
};

template< typename T >
inline void
partial_specialization_class_foo( T const& arg )
{
    Foo< T >::foo( arg );
}

void partial_specialization_class_example()
{
    partial_specialization_class_foo( int() );
    partial_specialization_class_foo( float() );
}

Note: in my real code, I'd have bar,baz, etc., along with foo static-methods.

FYI, this is C++03.

As an aside, am I doing the templated function overloading in the conventional way?

3条回答
smile是对你的礼貌
2楼-- · 2019-06-04 13:26

IntegralTypeTag in the enable_if will get in the way. The default for the second parameter of Foo is void, which is not the same as IntegralTypeTag, so the specialization of Foo will fail to match.

Ie, Foo< int, void > (which is what you get when you do Foo<int>) does not match Foo< int, IntegralTypeTag >, which your intended int specialization is (after the enable_if logic).

Tagging is the result of a type_traits class, which you can then use to simplify other type_traits classes.

查看更多
我想做一个坏孩纸
3楼-- · 2019-06-04 13:32

Here is one approach:

#include <tr1/type_traits>
#include <iostream>

struct IntegralTypeTag;
struct FloatingPointTypeTag;

template <
  typename T,
  bool is_integral = std::tr1::is_integral<T>::value,
  bool is_floating_point = std::tr1::is_floating_point<T>::value
> struct TypeTag;

template <typename T>
struct TypeTag<T,true,false> {
  typedef IntegralTypeTag Type;
};

template <typename T>
struct TypeTag<T,false,true> {
  typedef FloatingPointTypeTag Type;
};

template <typename T,typename TypeTag = typename TypeTag<T>::Type> struct Foo;


template <typename T>
struct Foo<T,IntegralTypeTag> {
  static void foo( T const& )
  {
    std::cout << "This is the integral partial-specialization class implementation.\n";
  }
};

template <typename T>
struct Foo<T,FloatingPointTypeTag> {
  static void foo( T const& )
  {
    std::cout << "This is the floating-point partial-specialization class implementation.\n";
  }
};

template< typename T >
inline void
partial_specialization_class_foo( T const& arg )
{
      Foo< T >::foo( arg );
}

int main(int,char**)
{
  partial_specialization_class_foo(int());
  partial_specialization_class_foo(float());
  return 0;
}
查看更多
趁早两清
4楼-- · 2019-06-04 13:38

After observing Vaughn's correct answer, I wanted to simplify it more. I was able to remove the use of Tags and extra trait-classes to come up with the following structure:

template< typename T, typename U = T >
struct Foo
{
};

template< typename T >
struct Foo< T, typename enable_if< std::tr1::is_integral< T >::value, T >::type >
{
    static void foo( T const& )
    {
        std::cout << "This is the integral partial-specialization class implementation.\n";
    }
};

template< typename T >
struct Foo< T, typename enable_if< std::tr1::is_floating_point< T >::value, T >::type >
{
    static void foo( T const& )
    {
        std::cout << "This is the floating-point partial-specialization class implementation.\n";
    }
};

template< typename T >
inline void
partial_specialization_class_foo( T const& arg )
{
    Foo< T >::foo( arg );
}

void partial_specialization_class_example()
{
    partial_specialization_class_foo( int() );
    partial_specialization_class_foo( float() );
}

I think this works by providing two template-type parameters to the class, where the second one is compile-time conditional:

  • This defaults to being same as the first parameter, so the caller only focuses on the first parameter.
  • Only partial-specializations that have the same second template param as the first can be matched.
  • When enable_if fails, the whole partial-specialization is unavailable to match against.

I find it less cumbersome to understand, comparatively.

查看更多
登录 后发表回答