用部分模板特化重写抽象成员函数

Overriding an abstract member function with partial template specialization

本文关键字:重写 抽象 成员 函数 用部      更新时间:2023-10-16

我正试图在c++中实现一个特定的模板设计,并且遇到了一个我想要解决的问题。下面是设置,后面是对问题的解释。

我像下面这样声明一个模板化的抽象基类:

template <class TModel, class TVertex>
class AttributeEvaluator2f
{
public:
    virtual void evaluate( TModel& model, TVertex& v, float x, float y ) = 0;
};

然后我想实现特殊化一个模板参数的子类,如下所示:

template <class TVertex>
class SurfacePositionFromSphere : public AttributeEvaluator2f<Sphere3f,TVertex>
{
public:
    virtual void evaluate( Sphere3f& sphere, TVertex& v, float theta, float phi )
    {
        sphere.SamplePosition( v.position, theta, phi );
    };
};

然后我将基于这些类实例化一个对象,如下所示:

SurfacePositionFromSphere<BasicVertexDX11::Vertex> attribute();

编译错误表明我从未在原始基类中实现抽象方法。这可能是因为在子类中,我直接用第一个参数声明方法,因为它已经专门化了(在本例中是Sphere3f)。

所以问题是,是否有可能以这种方式重写抽象基类方法参数的类型?如果没有,是否有类似的机制可以用来提供类似的功能?

提前感谢您的帮助!

编辑:确切的错误信息是巨大的,由于模板展开,但这里是它的主要部分供您参考:错误C2259: 'Glyph3::AttributeEvaluator2f':无法实例化抽象>类1> 与1> [1> TModel = Glyph3:: Sphere3f,1> TVertex = Glyph3: BasicVertexDX11::顶点1> ]1>由于以下成员:1> 'void Glyph3::AttributeEvaluator2f::evaluate(TModel &,TVertex &,float,float)':是抽象的1> 与1> [1> TModel = Glyph3:: Sphere3f,1> TVertex = Glyph3: BasicVertexDX11::顶点1> ]1> c:userszij1fhdocumentsvisual studio 2012projectshg3trunkhieroglyph3sourcerendering AttributeEvaluator2f .h(26):见"Glyph3::AttributeEvaluator2f::evaluate"声明1> 与1> [1> TModel = Glyph3:: Sphere3f,1> TVertex = Glyph3: BasicVertexDX11::顶点1> ]1> c:program files (x86)microsoft visual studio 11.0vcincludexmemory0(751):参见参考函数模板实例化'void std::allocator<_Ty>::construct<_Objty,_Ty>(_Objty *,_V0_t &&)'正在编译1> 与1> [1> _ 泰= Glyph3:: AttributeEvaluator2f,1> _ Objty = Glyph3:: AttributeEvaluator2f,1> _ V0_t = Glyph3:: AttributeEvaluator2f1> ]1> c:program files (x86)microsoft visual studio 11.0vcincludexmemory0(751):参见参考函数模板实例化'void std::allocator<_Ty>::construct<_Objty,_Ty>(_Objty *,_V0_t &&)'正在编译1> 与1> [1> _ 泰= Glyph3:: AttributeEvaluator2f,1> _ Objty = Glyph3:: AttributeEvaluator2f,1> _ V0_t = Glyph3:: AttributeEvaluator2f1> ]1> c:program files (x86)microsoft visual studio 11.0vcincludexmemory0(903):参见参考函数模板实例化'void std::allocator_traits<_Alloc>::construct<_Ty>(std:: allocator_trait<_Ty>,_Objty *,_V0_t &&)'正在编译

EDIT2:上面指出的用法是不正确的(Andy指出了)。这就是我的用法:

VertexEvaluator2f< Sphere3f, BasicVertexDX11::Vertex > evaluator;
evaluator.Evaluators.push_back( SurfacePositionFromSphere<BasicVertexDX11::Vertex>() );

在VertexEvaluator2f类中,我遍历属性以生成顶点。为了完整起见,下面也是该类的声明:

template <class TModel, class TVertex>
class VertexEvaluator2f
{
public:
    void SetModel( TModel& model ) {
        m_Model = model;
    };
    void evaluate( TVertex& v, float x, float y ) {
        for ( auto& evaluator : Evaluators ) {
            evaluator.evaluate( m_Model, v, x, y );
        }
    };
    std::vector< AttributeEvaluator2f< TModel, TVertex > > Evaluators;
protected:
    TModel m_Model;
};
所以问题是,是否有可能以这种方式重写抽象基类方法参数的类型?

是的,有可能。您的类的定义看起来是正确的,并且您可以看到它们在这里被正确编译(在这里我使用了一些虚拟的VertexSphere3f类)。

问题很可能(或者至少也是)在于这一行:

SurfacePositionFromSphere<BasicVertexDX11::Vertex> attribute();

实际上声明了一个函数命名为attribute,它不接受参数并返回一个类型为SurfacePositionFromSphere<BasicVertexDX11::Vertex>的对象。

当您稍后尝试访问该对象的成员函数时,编译器会报错(毕竟,您正在尝试访问函数指针的某些成员函数,这对编译器来说是无意义的)。

为了创建对象而不是声明函数,请删除对象名称后的括号:

SurfacePositionFromSphere<BasicVertexDX11::Vertex> attribute;
//                                                          ^
//                                             No parentheses

更新:

你是这样声明你的属性向量的:

std::vector< AttributeEvaluator2f< TModel, TVertex > > Evaluators;
与所有标准容器一样,

std::vector<>具有值语义。这意味着它将包含类型为AttributeEvaluator2f< TModel, TVertex >的对象(而不是指向对象的指针!),并且该类确实是抽象的。

在这种情况下你需要使用(智能)指针,例如:

#include <memory>
template <class TModel, class TVertex>
class VertexEvaluator2f
{
    // ...
    void evaluate( TVertex& v, float x, float y ) {
        for ( auto& evaluator : Evaluators ) {
            evaluator->evaluate( m_Model, v, x, y );
        //           ^^
        }
    };
    std::vector<
        std::shared_ptr<AttributeEvaluator2f<TModel, TVertex>>
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        > Evaluators;
protected:
    TModel m_Model;
};

下面是添加元素到集合的方法:

VertexEvaluator2f< Sphere3f, BasicVertexDX11::Vertex > evaluator;
evaluator.Evaluators.push_back( 
    std::make_shared<SurfacePositionFromSphere<BasicVertexDX11::Vertex>>()
    );