简体   繁体   中英

How do I get this template class to compile for Classes and primitive types?

How do I get the following code to compile?

I realize that the compiler is not happy with V<double> because it tries to compile my typedef for GetterFn , but I would like GetterFn and GetCalc() to be available for classes, but ignored for primitive types.

How should I recode this class?

#include <vector>

using namespace std;

class Bar
{
    public:
        float getMyFloat() const { return 42.5; }
};

template< typename T >
class V
{
    public:
        typedef float (T::*GetterFn)() const;
        void getCalc( std::vector<double>& vec, GetterFn fn ) const
        {
            vec.clear();
            for ( size_t i=0; i<m_v.size(); ++i )
                vec.push_back( m_v[ i ].*(fn)() );
        }

    private:
        vector<T> m_v;
};

int main(int argc, char** argv)
{
    V<Bar>    vb;  // ok
    V<double> vd;  // compiler not happy
}

Alf's slightly snarky answer is indeed the easiest way. Instead of having the GetterFn be a member function pointer, just use an overloaded free function to get the value:

void getCalc( std::vector<double>& vec) const
{
    vec.clear();
    for ( size_t i=0; i<m_v.size(); ++i )
        vec.push_back( get_value(m_v[ i ]) );
}

and then just overload get_value appropriately:

double get_value(double value) { return value; }

double get_value(Bar value) { return value.getMyFloat(); }

Of course, a more descriptive name than get_value is highly recommended.

A double cannot have a member function. Ergo, use a non-member function.

It's a bit hard to suggest the complete solution without knowing what you want to do for fundamental types. However, at the heart I suggest you employ the std::is_fundamental type trait.

If you want entire collections of member functions to exist only conditionally, then perhaps wrapping them into a member helper class is one idea:

#include <vector>
#include <type_traits>
#include <cstddef>

class Bar
{
public:
  float getMyFloat() const { return 42.5; }
};

template< typename T >
class V
{
private:

  static const bool m_primitive = std::is_fundamental<T>::value; // convenience

  template <bool B, typename U> struct Helper;

  template <typename U> struct Helper<false, U>
  {
    typedef float (T::*GetterFnType)() const;
    void getCalc(std::vector<double> & v1, std::vector<U> const & v2, GetterFnType f)
    {
      v1.clear();
      for (std::size_t i = 0; i < v2.size(); ++i)
        v1.push_back((v2[i].*f)());
    }
  };

public:

  // use Helper<m_primitive, T>::GetterFn and Helper<m_primitive, T>::getCalc() here

private:
  std::vector<T> m_v;
};

int main(int argc, char** argv)
{
  V<Bar>    vb;  // ok
  V<double> vd;  // compiler also happy
}

You'll probably also have to do some std::enable_if in the actual implementation. If you post more details, we can elaborate.

If you're open to a broader redesign, Konrad's answer feels like the final result would be somewhat cleaner and simpler, but then again I'm all in favour of convoluted template voodoo :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM