Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object slicing when using std::enable_if

I'm attempting to use std::enable_if to specialise a class if one of it's subclasses has a specific member function defined. Otherwise it should use a default implementation that is defined in the base class.

#include <boost/mpl/list.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/tti/has_member_function.hpp>

#include <iostream>
#include <type_traits>
#include <memory>

BOOST_TTI_HAS_MEMBER_FUNCTION(f2)

class Base
{
public:
    virtual double f1(double x, double y) const
    {
        std::cout << "Called Base Method" << std::endl;
        return 0.0;
    }
};

template<typename Derived>
class A : public Base
{
public:
    template<typename T = Derived>
    typename std::enable_if
              < has_member_function_f2< T
                                      , double
                                      , boost::mpl::list<double>
                                      , boost::function_types::const_qualified
                                      >::value
              , double
              >::type
    f1(double x, double y) const
    {
        std::cout << "Called Derived Method" << std::endl;
        return static_cast<const Derived* const>(this)->f2(x);
    }
};


class B : public A<B>
{
public:
    double f2(double x) const
    {
        return 1.0;
    }
};

int main()
{
    std::unique_ptr<Base> base_instance( new B );
    std::cout << base_instance->f1(100.0, 10.0) << std::endl;

    B b_instance;
    std::cout << b_instance.f1(100.0, 10.0) << std::endl;

    return 0;
}

I would have expected this to print

Called Derived Method
1
Called Derived Method
1

however instead I get

Called Base Method
0
Called Derived Method
1

so it looks like some object slicing is occurring. I can't for the life of me see why this would be the case, if anyone could help me that would be greatly appreciated.

If it helps in any way this is being compiled with g++ 4.7.2

like image 855
Simon Gibbons Avatar asked Dec 15 '14 17:12

Simon Gibbons


1 Answers

Template functions cannot be virtual. That also means that a template function will never override a virtual function in the base class, even if its particular instantiation signature happens to match.

To achieve what you want, you need to specialize A as a whole, to provide the member in one version and not in the other.

like image 155
Sebastian Redl Avatar answered Oct 03 '22 00:10

Sebastian Redl