在不使用c ++中的虚函数的情况下从模板化派生类访问方法?

时间:2009-10-14 14:04:30

标签: c++ inheritance templates virtual abstract-class

我如何解决这个问题?我显然无法将value()方法设为虚拟,因为我事先不知道它是什么类型,并且在从b访问方法时可能不知道这一点:

class Base
{
public:
    Base() { }
    virtual ~Base() { }
private:
    int m_anotherVariable;
};

template <typename T>
class Derived : public Base
{
public:
    Derived(T value) : m_value(value) { }
    ~Derived() { }

    T value() { return m_value; }
    void setValue(T value) { m_value = value; }
private:
    T m_value;
};

int main()
{
    Base* b = new Derived<int>(5);

    int v = b->value();

    return 0;
}

编译错误:

error: 'class Base' has no member named 'value'

8 个答案:

答案 0 :(得分:3)

本声明:

int v = b->value();

变量'b'被捕获,就像它是Derived&lt; int&gt;的对象一样 所以告诉编译器:

int v = dynamic_cast<Derived<int>*>(b)->value();

注意:如果b不是Derived&lt; int&gt;演员的结果是NULL 所以以下可能会更安全:

Derived<int>*  d = dynamic_cast<Derived<int>*>(b);
if (d)
{
    int v = d->value();
}
else
{
    // Error
}

或者通过使用引用,您将获得抛出的bad_cast异常:

// Throw bad_cast on failure.
Derived<int>& d = dynamic_cast<Derived<int>&>(*b);
int v = d->value();

或者为了善良和模糊,我们可以做到一行。

// Assign v or throw bad_cast exception:
int v = dynamic_cast<Derived<int>&>(*b).value();

但我认为你可以通过boost :: any

实现你想要做的事情
int main()
{
    boost::any   b(5);

    int    v = boost::any_cast<int>(b);

    b        = 5.6; // double
    double d = boost::any_cast<double>(b);
}

答案 1 :(得分:2)

如果您提出这个问题(根据我的经验,我的设计),我认为可能会对您的设计造成一些问题。
但是,有一些解决方法:

  1. 正如其他人已经回答的那样,您可以将Base设为类模板。
  2. 你可以低估b(虽然我更愿意避免向下倾斜)
  3. 您可以将value()声明为在Base中返回boost :: any并使其成为纯虚拟
  4. 您甚至可以将其声明为返回void *(但不要这样做。这是一个选项,但不是一个选项)
  5. 但这里真正的问题是你试着在这里描述什么? Base,Derived和value()的含义是什么?问自己这些问题,你可能不需要这些答案......

答案 2 :(得分:1)

一些解决方案:

template < typename T>
class Base{
public: 
    Base() { }    
    virtual ~Base() { }
    virtual T value() = 0;
private:    
    int m_anotherVariable;
};

template <typename T>
class Derived : public Base<T> {
  ...
}

int main(){    
    Base<int>* b = new Derived<int>(5);    
    int v = b->value();    
    return 0;
}

另一种解决方案:

class Base {
public: 
    Base() { }    
    virtual ~Base() { }
    template<class T> T value() const;
private:    
    int m_anotherVariable;
};

template <typename T>
class Base2 : public Base {
public: 
    Base2() { }    
    virtual ~Base2() { }
    virtual T getValue() const = 0;    
};

template<class T> T Base::value() const {
    const Base2<T> * d = dynamic_cast<const Base2<T> *>(this);
    return d ? d->getvalue() : T();
}

template <typename T>
class Derived : public Base2<T> {
public:    
    Derived(T value) : m_value(value) { }    
    virtual ~Derived() { }    
    void setValue(T value) { m_value = value; }
    virtual T getValue() const { return  m_value; }    
private:    
     T m_value;
}

int main(){    
    Base* b = new Derived<int>(5);    
    int v = b->value<int>();
    return 0;
}

答案 3 :(得分:0)

在这种情况下,Base需要知道模板类型,因此b-&gt; value返回T。

我建议将模板添加到Base,然后在Base

上将值设为虚函数

答案 4 :(得分:0)

您可以将指针投射到Derived

int v = dynamic_cast<Derived<int>*>(b)->value();

当然,在实际代码中,您必须添加检查以确保dynamic_cast没有失败。

答案 5 :(得分:0)

为什么不制作基于模板的基类呢?然后,您可以将值作为虚拟成员。

或者你可以将b转发给Derived。

答案 6 :(得分:0)

看起来你想要动态多态,但只使用模板的“静态多态”。

如果你想要动态多态,你确实需要基类中的虚拟成员(以及你的虚拟析构函数)或者如果你没有公共接口那么需要down_cast。

如果没有,请删除虚拟析构函数,仅使用指向派生类型的指针或实例。

关于Base作为模板:

它将阻止您将动态多态性的单个基类作为Base&lt; int&gt;和基地&lt;其他&gt;将是不兼容的。

答案 7 :(得分:0)

处理此问题的一种方法是通过visitor pattern。基本思想是,在您的类层次结构中,您实现了一个接受访问者的onNode函数。然后,您编写符合您需要的特定访问者。在你的情况下,你最终得到:

class GetIntValue : public BaseVisitor
{
public:
  GetIntValue (int & result)
  : m_result (result) {}

  void onNode (Derived<int> & d)
  {
    m_result = d.m_value;
  }
  void onNode (Derived<A> & d)
  {
    assert (! "calling GetIntValue on a node that doesn't have a value");
  }


private:
  int & m_result;
};

int main()
{
  Base* b = new Derived<int>(5);

  int v;
  GetIntValue(v).visit (*b);

  return 0;
}