Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/155.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用函数指针的模板化双重分派 我试图为学术目的制作一个自定义碰撞引擎,我被困在一个通用的C++编程问题上。我已经有了所有正常工作的几何图形,碰撞测试也正常工作_C++_Templates_C++11_Function Pointers_Double Dispatch - Fatal编程技术网

使用函数指针的模板化双重分派 我试图为学术目的制作一个自定义碰撞引擎,我被困在一个通用的C++编程问题上。我已经有了所有正常工作的几何图形,碰撞测试也正常工作

使用函数指针的模板化双重分派 我试图为学术目的制作一个自定义碰撞引擎,我被困在一个通用的C++编程问题上。我已经有了所有正常工作的几何图形,碰撞测试也正常工作,c++,templates,c++11,function-pointers,double-dispatch,C++,Templates,C++11,Function Pointers,Double Dispatch,引擎使用这两个类来创建要测试的几何体队列: class collidable; template<typename geometry_type> class collidable_object : public collidable; 类可碰撞; 模板 类可碰撞对象:公共可碰撞; 由于可以使用多种几何体类型,我不想手动指定任何要测试的碰撞 相反,我使用这种“技术”来实现双重分派: class collidable { public: typedef bool (coll

引擎使用这两个类来创建要测试的几何体队列:

class collidable;

template<typename geometry_type>
class collidable_object : public collidable;
类可碰撞;
模板
类可碰撞对象:公共可碰撞;
由于可以使用多种几何体类型,我不想手动指定任何要测试的碰撞

相反,我使用这种“技术”来实现双重分派:

class collidable
{
public:
    typedef bool (collidable::*collidable_hit_function)(const collidable& ) const;

    virtual ~collidable() = 0 {}

    virtual collidable_hit_function get_hit_function() const = 0;
};

template<typename geometry_type>
class collidable_object : public collidable
{
public:
    explicit collidable_object( geometry_type& geometry ) :
        m_geometry( geometry )
    {}

    ~collidable_object(){}

    virtual collidable_hit_function get_hit_function() const
{
    return static_cast<collidable_hit_function>( &collidable_object<geometry_type>::hit_function<geometry_type> );
}

template<typename rhs_geometry_type>
bool hit_function( const collidable& rhs ) const
{
    return check_object_collision<geometry_type, rhs_geometry_type>( *this, rhs );
}

const geometry_type& geometry() const
{
    return m_geometry;
}

private:
    geometry_type& m_geometry;
};

bool check_collision( const collidable& lhs, const collidable& rhs )
{
    collidable::collidable_hit_function hit_func = lhs.get_hit_function();

    return (lhs.*hit_func)( rhs );
}
类可碰撞
{
公众:
typedef bool(可碰撞::*可碰撞的命中函数)(const可碰撞&)const;
虚~可碰撞()=0{}
虚拟可碰撞_hit_函数get_hit_函数()const=0;
};
模板
类可碰撞对象:公共可碰撞对象
{
公众:
显式可碰撞对象(几何体类型和几何体):
m_几何(几何)
{}
~collidable_object(){}
虚拟可碰撞_hit_函数get_hit_函数()常量
{
返回静态_cast(&可碰撞的_对象::命中_函数);
}
模板
布尔命中函数(常数可碰撞和rhs)常数
{
返回检查对象碰撞(*此,rhs);
}
常量几何体类型和几何体()常量
{
返回m_几何体;
}
私人:
几何图形类型&m几何图形;
};
布尔检查碰撞(常数可碰撞和左侧碰撞、常数可碰撞和右侧碰撞)
{
collidable::collidable_hit_函数hit_func=lhs.get_hit_函数();
返回(左侧。*命中函数)(右侧);
}
其中,
check\u object\u collision
函数是一个模板函数,用于测试碰撞并已测试

我的问题如下:函数get\u hit\u函数中的强制转换确实可以编译,但看起来可疑。。。我是否做了一些可怕的错误,这将导致未定义的行为和多个噩梦,或者是否可以将模板成员函数指针从一个派生类转换到另一个派生类

<> P>我困惑的是,在VisualC++ 2012中编译和<强> > 可正常工作…< /P> 是什么让这个演员出了大问题

我真的不明白强制转换函数指针意味着什么


作为后续问题,是否有一种安全的方法来实现这一点?Q:可以将模板成员函数指针从一个派生类强制转换到另一个派生类吗

答:是的,如果get_hit_函数()确实兼容的话


如果这是Java或C#,我只需要声明一个接口:)

可以将指向方法的指针从基类强制转换为派生类。相反,这是一个非常糟糕的主意。想想如果有人像这样使用你的代码会发生什么:

collidable_object<A> a;
collidable_hit_function f = a.get_hit_function();

collidable_object<B> b;
b.*f(...);
然后你最终会称之为:

check_object_collision<A, A>();
检查对象碰撞();
因此,您正在检查碰撞,好像两个可碰撞的
都是几何体
A
-我猜这不是您想要做的


这是一个您可能无法用任何单一语言构造解决的问题,因为它需要不同碰撞检查的二维数组,每对几何体一个,并且您需要类型擦除才能操纵通用的可碰撞的标准明确允许const
to
bool(collidable::*)()const
,因为
collidable
collidable\u对象的一个可访问的明确非虚拟基类

将指向成员的指针转换为相反方向(从派生到基础)时,不需要静态转换。这是因为存在从
bool(可碰撞::*)()常量到
bool(可碰撞对象::*)()常量的有效“标准转换”

[conv.mem]

类型为“指向cv T类型的B的成员的指针”的右值(其中B是类类型)可以转换为类型为“指向cv T类型的D的成员的指针”的右值,其中D是B的派生类。如果B是D的不可访问、不明确或虚拟基类,则需要此转换的程序格式不正确。转换的结果引用的成员与转换发生前指向成员的指针相同,但它引用的基类成员与派生类的成员相同。结果引用了D的B实例中的成员。[……]

由于存在此有效的标准转换,并且
collidable
collidable\u对象
的一个可访问的明确非虚拟基类,因此可以使用
static\u cast
从基转换为派生

[expr.static.cast]

如果存在从“指向类型T的B的成员的指针”到“指向类型T的D的成员的指针”的有效标准转换,并且cv2与以下条件相同,则类型“指向类型cv1 T的D的成员的指针”的右值可以转换为类型“指向类型cv2 T的B的成员的指针”的右值,其中B是D的基类,或大于cv1的cv资格。[…]如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,则指向该成员的结果指针将指向原始成员。否则,强制转换的结果是未定义的。[……]


当通过指向基类成员的指针调用派生类成员函数时,必须确保调用它的基类对象是派生类的实例。否则,将出现未定义的行为

这是一个例子。取消对main最后一行的注释说明了未定义的行为-不幸的是,这破坏了查看器

编译器在后台做了什么来实现这一点?这是一个完全不同的问题;)

关于后续问题,实现双重分派的标准方法是使用访问者模式。以下是如何将其应用于您的场景的示例:

#include <iostream>

struct Geom
{
    virtual void accept(Geom& visitor) = 0;
    virtual void visit(struct GeomA&) = 0;
    virtual void visit(struct GeomB&) = 0;
};

struct GeomA : Geom
{
    void accept(Geom& visitor)
    {
        visitor.visit(*this);
    }
    void visit(GeomA& a)
    {
        std::cout << "a -> a" << std::endl;
    }
    void visit(GeomB& b)
    {
        std::cout << "a -> b" << std::endl;
    }
};

struct GeomB : Geom
{
    void accept(Geom& visitor)
    {
        visitor.visit(*this);
    }
    void visit(GeomA& a)
    {
        std::cout << "b -> a" << std::endl;
    }
    void visit(GeomB& b)
    {
        std::cout << "b -> b" << std::endl;
    }
};

void collide(Geom& l, Geom& r)
{
    l.accept(r);
}


int main()
{
    GeomA a;
    GeomB b;
    
    collide(a, a);
    collide(a, b);
    collide(b, a);
    collide(b, b);
}

#include <iostream>

struct Geom
{
    virtual void accept(Geom& visitor) = 0;
    virtual void visit(struct GeomA&) = 0;
    virtual void visit(struct GeomB&) = 0;
};

struct GeomA : Geom
{
    void accept(Geom& visitor)
    {
        visitor.visit(*this);
    }
    void visit(GeomA& a)
    {
        std::cout << "a -> a" << std::endl;
    }
    void visit(GeomB& b)
    {
        std::cout << "a -> b" << std::endl;
    }
};

struct GeomB : Geom
{
    void accept(Geom& visitor)
    {
        visitor.visit(*this);
    }
    void visit(GeomA& a)
    {
        std::cout << "b -> a" << std::endl;
    }
    void visit(GeomB& b)
    {
        std::cout << "b -> b" << std::endl;
    }
};

void collide(Geom& l, Geom& r)
{
    l.accept(r);
}


int main()
{
    GeomA a;
    GeomB b;
    
    collide(a, a);
    collide(a, b);
    collide(b, a);
    collide(b, b);
}