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

发布于 2024-08-07 05:36:14 字数 628 浏览 6 评论 0原文

我该如何解决这个问题?我显然无法将 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'

How do I get around this? I clearly cannot make the value() method virtual as I won't know what type it is beforehand, and may not know this when accessing the method from 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;
}

Compilation errors:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

骄傲 2024-08-14 05:36:14

此语句:

int v = b->value();

变量“b”被视为 Derived的对象。
所以告诉编译器:

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

注意:如果 b 不是 Derived;转换结果为 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);
}

This statement:

int v = b->value();

The variable 'b' is being trated like it is an object of Derived<int>.
So tell the compiler:

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

Note: If b is not a Derived<int> the result of the cast is NULL.
So the following would probably be safer:

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

Alternatively by using references you get a bad_cast exception thrown:

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

Or to be nice and obscrure we can do it one line.

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

But I think you can achieve what you are trying to do with the 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);
}
谜泪 2024-08-14 05:36:14

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

  1. 正如其他人已经回答的那样,您可以将 Base 设为类模板。
  2. 您可以向下转型 b (尽管我更愿意尽可能避免向下转型)
  3. 您可以在 Base 中声明 value() 返回 boost::any 并使其成为纯虚拟
  4. 您甚至可以将其声明为返回 void* (但不要这样做这是一种选择,但不是一个好选择)

但这里真正的问题是你想在这里描述什么? Base、Derived 和 value() 的含义是什么。问自己这些问题,你可能不需要这些答案......

I think there may be some problems with your design if you ask this question (from my experience, with my designs).
However, there are some workarounds:

  1. As other people already answered, you can make Base a class template.
  2. You can downcast b (although I prefer to avoid downcasts if I can)
  3. You can declare value() as returning boost::any in Base and make it pure virtual
  4. You can even declare it as returning void* (but don't do that. It's an option, but a bad one)

But the real question here is what do you try to describe here? What is the meaning of Base, Derived and value(). Ask yourself these question and you may not need these answers...

标点 2024-08-14 05:36:14

一些解决方案:

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;
}

Some solution:

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;
}

Another solution:

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;
}
Hello爱情风 2024-08-14 05:36:14

在这种情况下,Base 将需要了解模板类型,因此 b->value 返回 T。

我建议将模板添加到 Base,然后将 value 设置为 Base 上的虚拟函数

In this case Base will need to know about the template type so b->value returns a T.

I would suggest adding the template to Base and then make value a virtual function on Base

八巷 2024-08-14 05:36:14

您可以将指针强制转换为 Derived

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

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

You could cast your pointer to Derived:

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

Of course in real code, you have to add checking to make sure that dynamic_cast didn't fail.

南街女流氓 2024-08-14 05:36:14

为什么不制作基于 Base 模板的类呢?那么您就可以拥有作为虚拟会员的价值。

或者您可以将 b 向下转型为 Derived。

Why don't make Base template-based class too? Then you can have value as a virtual member.

Or you can downcast b to Derived.

残疾 2024-08-14 05:36:14

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

如果您想要动态多态性,您确实需要基类中的虚拟成员(以及虚拟析构函数)或 down_cast (如果您没有通用接口)。

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

关于 Base 作为模板:

它将阻止您拥有用于动态多态性的单个基类,如 Base<;整数>且基数<其他>将会不兼容。

It looks like you want dynamic polymorphism but using only the "static polymorphism" of templates.

If you want dynamic polymorphism you do need virtual members in your base class ( along with your virtual destructor ) or the down_cast if you do not have a common interface.

If not, remove the virtual destructor and use only pointers to or instances of derived types.

About Base as a template :

It will prevent you from having a single base class for dynamic polymorphism as Base< int > and Base< other > will be incompatibles.

雨落星ぅ辰 2024-08-14 05:36:14

处理此问题的一种方法是通过访问者模式。基本思想是,在类的层次结构中实现一个接受访问者的 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;
}

One way of handling this is via the visitor pattern. The basic idea is that in your hierarchy of classes you implement an onNode function that accepts a visitor. Then, you write specific visitors that do what you want. In your case, you'd end up with:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文