使用std :: enable_if时的对象切片

时间:2014-12-15 17:02:27

标签: c++ c++11 virtual-functions enable-if object-slicing

如果其中一个子类具有定义的特定成员函数,我试图使用std::enable_if来专门化一个类。否则,它应该使用在基类中定义的默认实现。

#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;
}

我原本希望打印

Called Derived Method
1
Called Derived Method
1

然而我得到了

Called Base Method
0
Called Derived Method
1

因此看起来正在进行某些对象切片。我不能为我的生活看到为什么会出现这种情况,如果有人能帮助我,我将不胜感激。

如果它以任何方式有帮助,那么使用g ++ 4.7.2编译它

2 个答案:

答案 0 :(得分:8)

模板功能不能是虚拟的。这也意味着模板函数永远不会覆盖基类中的虚函数,即使它的特定实例化签名恰好匹配。

要实现您的目标,您需要将A作为一个整体进行专门化,以便在一个版本中提供该成员而在另一个版本中不提供。

答案 1 :(得分:3)

@Sebastian's answer解释了这个问题,但建议的解决方案会有问题:你不能使用CRTP专门化派生类的属性上的基类模板,因为派生类在完成时不完整基类被实例化。我建议你改为始终覆盖f1中的A,并使用标签调度来确定是派遣到派生类中的f2还是默认实现在Base

template<typename Derived>
class A : public Base
{
    double f1_impl(boost::mpl::true_, double x, double) const
    {
        std::cout << "Called Derived Method\n";
        return static_cast<const Derived*>(this)->f2(x);
    }
    double f1_impl(boost::mpl::false_, double x, double y) const
    {
        return Base::f1(x, y);
    }

public:
    double f1(double x, double y) const override
    {
        using has_f2 = typename has_member_function_f2
            < Derived
            , double
            , boost::mpl::list<double>
            , boost::function_types::const_qualified
            >::type;
        return f1_impl(has_f2{}, x, y);
    }
};

DEMO