Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to conditionally compile member based on class template [duplicate]

In C++ if you want to partially specialize a single method in a template class you have to specialize the whole class (as stated for example in Template specialization of a single method from templated class with multiple template parameters)

This however becomes tiresome in bigger template classes with multiple template parameters, when each of them influences a single function. With N parameters you need to specialize the class 2^N times!

However, with the C++11 I think there might a more elegant solution, but I am not sure how to approach it. Perhaps somehow with enable_if? Any ideas?

like image 835
CygnusX1 Avatar asked Nov 23 '22 19:11

CygnusX1


2 Answers

In addition to the inheritance-based solution proposed by Torsten, you could use std::enable_if and default function template parameters to enable/disable certain specializations of the function.

For example:

template<typename T>
struct comparer
{
    template<typename U = T ,
    typename std::enable_if<std::is_floating_point<U>::value>::type* = nullptr>
    bool operator()( U lhs , U rhs )
    {
        return /* floating-point precision aware comparison */;
    }

    template<typename U = T ,
    typename std::enable_if<!std::is_floating_point<U>::value>::type* = nullptr>
    bool operator()( U lhs , U rhs )
    {
        return lhs == rhs;
    } 
};

We take advantage of SFINAE to disable/enable the different "specializations" of the function depending on the template parameter. Because SFINAE can only depend on function parameters, not class parameters, we need an optional template parameter for the function, which takes the parameter of the class.

I prefer this solution over the inheritance based because:

  • It requires less typing. Less typing probably leads to less errors.
  • All specializations are written inside the class. This way to write the specializations holds all of the specializations inside the original class , and make the specializations look like function overloads, instead of tricky template based code.

But with compilers which have not implemented optional function template parameters (Like MSVC in VS2012) this solution does not work, and you should use the inheritance-based solution.

EDIT: You could ride over the non-implemented-default-function-template-parameters wrapping the template function with other function which delegates the work:

template<typename T>
struct foo
{
private:
    template<typename U>
    void f()
    {
        ...
    }

public:
    void g()
    {
        f<T>();
    }
};

Of course the compiler can easily inline g() throwing away the wrapping call, so there is no performance hit on this alternative.

like image 107
Manu343726 Avatar answered Nov 25 '22 10:11

Manu343726


One solution would be to forward from the function, you want to overload to some implementation that depends on the classes template arguments:

template < typename T >
struct foo {
   void f();
};

template < typename T >
struct f_impl {
    static void impl()
    {
        // default implementation
    }
};

template <>
struct f_impl<int> {
    static void impl()
    {
         // special int implementation
    }
};

template < typename T >
void foo< T >::f()
{
    f_impl< T >::impl();
}

Or just use private functions, call them with the template parameter and overload them.

template < typename T >
class foo {
public:
    void f()
    {
        impl(T());
    }
private:
    template < typename G >
    void impl( const G& );
    void impl( int );
};

Or if it's really just one special situation with a very special type, just query for that type in the implementation.

like image 38
Torsten Robitzki Avatar answered Nov 25 '22 09:11

Torsten Robitzki