【问题标题】:Object slicing when using std::enable_if使用 std::enable_if 时的对象切片
【发布时间】:2014-12-15 17:02:27
【问题描述】:

如果其中一个子类定义了特定的成员函数,我正在尝试使用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 进行编译

【问题讨论】:

  • 是什么让你认为你应该如你所愿?
  • 我有一种感觉,我误解了我会离开std::enable_if 的类型,但我认为 B 中 f1 的类型是 double f1(double, double) const,因此应该选择作为虚函数。

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    @Sebastian's answer 解释了这个问题,但建议的解决方案将是有问题的:您不能使用 CRTP 在派生类的属性上专门化基类模板,因为派生类在基类不完整时被实例化。我建议您改为始终 覆盖A 中的f1,并使用标签调度来确定是调度到派生类中的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

    【讨论】:

    • 谢谢,我不知道基于标签的调度是一回事。这解决了我的问题:)
    猜你喜欢
    • 1970-01-01
    • 2014-08-29
    • 1970-01-01
    • 2016-04-27
    • 1970-01-01
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多