2015-11-06 73 views
2

我试图创建一个非模板基类来创建一个接口,通过它我可以与派生模板类进行交互。我的希望是使用部分虚拟函数重载,但有些不起作用,我不知道为什么。 任何人都可以解释为什么我下面的代码返回:如何在C++模板子类中部分重载虚函数?

B match = False 
D match = True 
E match = False 

,而不是B,d返回true,和E返回false?我本以为在派生类中的重载操作符会拿起指针到int'&我'并被调用,但它不。

为了清楚起见,我没有试图覆盖基本版本的匹配,我正在尝试对它进行过载,特别是希望它有一个不同的接口,在这种情况下,接口要比专用接口更多在Base中,它在使用其功能签名时接管。

我也试图避免为每个可能实例化的派生模板扩展基类。

即使是陌生人,我也可能会在这里疯狂,但我发誓这在不久前的某个时间对我有用! Fwiw我在OSX 10.5上,使用llvm 7.0.0和clang 700.1.76。这可能是一个编译器特质?

虽然我试图(不成功)在这里使用部分重载,但我真的很开放,可以解决按照其参数类型选择模板实例函数的问题,而不会增加类,如果-thens /或者向基类添加特定的特化。如果您有另一种方法可以用于类似功能,我很乐意听到它。

谢谢您提供的任何见解!

#include <stdio.h> 

class Base 
{ 
public: 
    Base() {} 
    virtual ~Base(){} 

    virtual bool match(const void *data) const { return false; } 
}; 

template <class Type> 
class Derived: public Base 
{ 
public: 
    Derived():Base() {} 
    ~Derived() override{} 

    virtual bool match(const Type *data) const { return true; } 
}; 

int main(int argc, char **argv) 
{ 
    Derived<int> *d = new Derived<int>(); 
    Derived<float> *e = new Derived<float>(); 
    Base *b   = d; 

    int i; 
    printf("B match = %s\n",b->match(&i)?"True":"False"); 
    printf("D match = %s\n",d->match(&i)?"True":"False"); 
    printf("E match = %s\n",e->match(&i)?"True":"False"); 

} 
+0

πάνταῥεῖ,好点!我忘了我可以编辑这个问题! :)修正。 – Dave

+0

你的虚拟功能并不纯粹。看看这是否给你一个合理的解决方案:http://stackoverflow.com/questions/2919584/override-number-of-parameters-of-pure-virtual-functions –

+1

@ o_weisman,感谢您的评论。使我的基本功能成为纯粹的虚拟似乎并不能解决这个设计问题。您提到的问题是值得深思的,但我无法弄清楚如何在没有为每个可能使用的派生类型增加特定的子类的情况下应用它。换句话说,如果没有内置到模板定义中的实例选择机制,则必须编写大量特定的类并进行实例化。尽管如此,谢谢你,我感谢你的帮助。 – Dave

回答

1

由于原来的问题下o_weisman的评论,我可以用函数模板找到一个有效的解决方案,(见下文)。无可否认,在我的解决方案的当前表单中,我利用了一个限制条件,即每个Derived的Type-in​​stance都是单例。这适用于我的特定设计,但行为可以根据需要进行扩展。一种可能允许派生多个实例的可能性可能是检查Base :: match中的'this'指针是否在一组所有实例中(在构造/破坏时更新的静态集合变量中跟踪),而不是一个单独的实例。无论如何,我希望这可以帮助那些可能面临类似设计挑战的人。

#include <stdio.h> 
#include <assert.h> 

template <class Type> class Derived; 
class Base 
{ 
public: 
    Base() {} 
    virtual ~Base(){} 

    template <class Type> 
    bool match(const Type *data) const { return (Derived<Type>::instance() == this); } 

}; 

template <class Type> 
class Derived: public Base 
{ 
public: 
    Derived(): Base() { assert(!ourInstance); ourInstance = this; } 
    ~Derived() override{} 

    static const Base *instance() { return ourInstance; } 

protected: 
    static const Derived<Type> *ourInstance; 

}; 

template <class Type> 
const Derived<Type> *Derived<Type>::ourInstance = NULL; 

int main(int argc, char **argv) 
{ 
    Derived<int> *d = new Derived<int>(); 
    Derived<float> *e = new Derived<float>(); 
    Base *b   = d; 

    int i; 
    printf("B match = %s\n",b->match(&i)?"True":"False"); 
    printf("D match = %s\n",d->match(&i)?"True":"False"); 
    printf("E match = %s\n",e->match(&i)?"True":"False"); 

} 

这产生期望的结果:

B match = True 
D match = True 
E match = False 
1

的这里的问题是,Derived::match不匹配Base::match。虚拟函数的函数签名必须与要工作的机制相同。所以你的代码Derived::match重载而不是覆盖它。

如果我们改变

virtual bool match(const Type *data) const { return true; } 

virtual bool match(const void *data) const { return true; } 

然后我们得到

B match = True 
D match = True 

Live Example

+0

谢谢你的想法弥敦道。为了清楚起见,我并没有试图覆盖基本版本的匹配,我正在尝试对它进行重载,特别是希望它有一个与Base中的不同的接口当它的功能签名被使用时接管。 – Dave

+0

@Dave这样做会打破你所看到的虚拟机制。如果该函数是一个重载,那么当你从一个'Base *'调用该函数时,将只考虑'Base *'函数。 – NathanOliver

+0

弥敦道,是的..它绝对破碎! :)你能想出任何其他方式,我可以从它的参数类型中选择派生的模板类函数吗? – Dave

2

如果你手动创建Derived<int>为一类,它的成员函数是:

virtual bool match(const int *data) const { return true; } 

它不会覆盖的基类。因此,当您使用基类指针调用函数时,它会执行基类实现,并且当您使用派生类指针调用它时,它会执行派生类实现。

如果您使用关键字override,则在编译时您将能够发现问题。

template <class Type> 
class Derived: public Base 
{ 
public: 
    Derived():Base() {} 
    ~Derived() override{} 

    virtual bool match(const Type *data) const override { return true; } 
}; 

你应该看到一个编译时错误的变化。

请参阅编译器错误http://ideone.com/8rBQ6B

更新,响应OP的评论

如果你不是说要重写:

  1. 不要在成员函数的声明中使用virtual
  2. 使用

    using Base::match 
    

    把基类的功能到派生类的范围以下是如何做到这一点:

    template <class Type> 
    class Derived: public Base 
    { 
    public: 
        Derived():Base() {} 
        ~Derived() override{} 
    
        using Base::match; 
        bool match(const Type *data) const { return true; } 
    }; 
    
+0

使用'override'关键字调用很好。 – GreatAndPowerfulOz

+0

感谢您的想法R Shau。为了清楚起见,我并没有试图覆盖基本版本的匹配,我正在尝试对它进行重载,特别是希望它有一个与Base中的不同的接口当它的功能签名被使用时接管。 – Dave

+0

@Dave,看到更新。 –

1

因为函数的签名不匹配:

class Base你有

virtual bool match(const void *data) const { return false; } 

注意const void *参数类型

然后Derived你有

virtual bool match(const Type *data) const { return true; } 

Type在这种情况下是int(从mainDerived<int> *d

只需添加一个虚拟的基础与const int* data签名,那么你会很好去。

是的,这确实意味着,在Base你必须添加的match过载,每Type你期望与Derived使用。

+0

感谢您的回答Gread.And.Powerful.Oz!我同意这样做,但它也需要我修改基类,使其对于我打算用于系统的每种类型都有一个特定的功能重载。这是我希望避免的。你能否想到另一种机制可以实现这一点,如果这不能呢? – Dave

+0

@Dave我唯一能想到的就是'bool Derived :: match(const Type * data){return match((void *)data); }'函数​​调用另一个'虚拟布尔衍生 :: match(const void * data)override {return true; }'。第一个功能是过载,第二个是覆盖。或者,换个角度来看看。 – GreatAndPowerfulOz

+0

再次感谢。 UNF。当我这样做,我得到误报:派生() - >匹配(&我)返回true,当我想它返回false(因为&我是一个int *不是浮动*)。 – Dave

1

这是行不通的原因是因为派生类实际上并不是覆盖基类中的虚函数。为了覆盖基类中的虚函数,派生类必须有正好相同的签名(请参阅下面有关例外的说明)。在这种情况下,签名是不同的,因为基类中的虚函数采用void *,但派生类中的函数采用type *

让我们做一个小的变化和override指令在派生类中添加函数签名:

#include <stdio.h> 

class Base 
{ 
public: 
    Base() {} 
    virtual ~Base(){} 

    virtual bool match(const void *data) const { return false; } 
}; 

template <class Type> 
class Derived: public Base 
{ 
public: 
    Derived():Base() {} 
    ~Derived() override{} 

    virtual bool match(const Type *data) const override { return true; } 
}; 

int main(int argc, char **argv) 
{ 
    Derived<int> *d = new Derived<int>(); 
    Base *b   = d; 

    int i; 
    printf("B match = %s\n",b->match(&i)?"True":"False"); 
    printf("D match = %s\n",d->match(&i)?"True":"False"); 

} 

这里发生了什么,当我们现在编译:

main.cpp: In instantiation of 'class Derived<int>': 
main.cpp:24:38: required from here 
main.cpp:19:16: error: 'bool Derived<Type>::match(const Type*) const [with Type = int]' marked 'override', but does not override 
    virtual bool match(const Type *data) const override { return true; } 

假设我们使用C++ 11使用override确保我们确实重写基类虚函数总是一个好主意。

来自上面的提示:有一种叫做covariant return type的地方,覆盖不必具有相同的签名,但这超出了问题的范围。

+0

感谢您的回答。我看了一下covariant返回类型 - 有趣。我想不出在上述情况下如何应用它,因为我试图根据参数的类型选择一个模板类方法到成员函数。好主意,但我学到了一些新东西! :) – Dave

+1

是的,对不起,我并不想暗示covariant返回类型解决了这个特殊问题。应该更清楚一点。我担心如果我没有提到它,有人可能会反对我在这个(多余的)基础上的回应。 –