可以在C++中完全避免模板吗?

Rya*_*ich 4 c++ polymorphism templates

我的问题,在最后一段,需要(在我看来)一些解释性设置.基本上,我想知道你是否可以避免使用模板而不是你创建所有你想成为的模板类而是从一个声明你将要使用的虚方法的基类继承,其中包括一个内存分配函数,当实现时,将返回指向派生(非基础)类型的指针.

BEGIN设置

C++似乎没有"通用基类"的概念,一切都是隐式派生的; 我想这个类定义如下:

class universal_base
{
};
Run Code Online (Sandbox Code Playgroud)

当然,现在我已经定义了它,我可以让我的所有类都来自它.然后,由于多态性,universal_base我传递的任何引用或指针将与模板参数基本相同:

template <typename T>

class C
{
   T &x;
   int f(T &y);
   C(T &z): x(z + 1) {}
};

class C
{
   universal_base &x;
   int f(universal_base &y);
   C(universal_base &z): x(z + 1) {}
};
Run Code Online (Sandbox Code Playgroud)

不同之处在于,在第一种结构中,表达式z + 1不能保证有效; 你只需告诉用户T必须超载operator+.在第二个结构中,我可以添加一个虚拟的运算符universal_base:

// in universal_base
public:
 virtual universal_base& operator+(const universal_base &x) = 0;
Run Code Online (Sandbox Code Playgroud)

并使用typeiddynamic_cast在实现中使争论正确.这样,编写格式错误的代码是不可能的,因为如果你没有实现,编译器会抱怨operator+.

当然,这样就无法声明非引用类型的成员:

class C: public universal_base
{
  universal_base x; // Error: universal_base is a virtual type
};
Run Code Online (Sandbox Code Playgroud)

但是,这可以通过仔细使用初始化来解决.事实上,如果我想为上面创建一个模板,

template <typename T>
class C: public universal_base
{
  T x;
};
Run Code Online (Sandbox Code Playgroud)

我几乎肯定会T在某个时候给它类型的对象.在这种情况下,我没有理由不能做到以下几点:

class universal_base
{
  public:
   virtual universal_base& clone() = 0;
};

class C: public universal_base
{
  universal_base &x;
  C(universal_base &y) : x(y.clone()) {}
}
Run Code Online (Sandbox Code Playgroud)

实际上,我创建了一个在运行时确定的类型变量.这当然要求对每个类型的对象C进行适当的初始化,但我不认为这是一个巨大的牺牲.

这不是学术性的,因为它具有以下用途:如果我正在编写一个旨在链接到其他程序并以某种通用方式处理其数据的模块,我不可能知道将要使用的类型.模板在这种情况下没有帮助,但上面的技术工作正常.

结束设置

所以,我的问题:这是否完全取代模板,模块化初始化的东西?某种程度上它效率低或危险吗?

Nem*_*emo 12

模板提供编译时多态性; 虚函数提供运行时多态性.

当然,在编译时你无法做任何在运行时无法做到的事情.不同之处是:

  1. 性能
  2. 编译时检查

您在使用typeiddynamic_cast将收取一定的运行时性能损失.一般的虚拟功能也是如此; 在现代CPU上,对可变位置的调用比调用固定位置要慢几百倍(因为它们往往会破坏指令预取机制).

因此,性能绝对是一个问题.

接下来......除非您强制每个类实现每个运算符,否则您将面临运行时检查失败的风险.如果模板试图调用+未实现它的类型,则结果将是编译时错误.

通常,模板的静态特性允许更好的编译时检查和优化.但是你的想法在语义上没有任何错误.


ssu*_*ube -2

您正在尝试编写一种古老而晦涩的语言,称为C. 有传言说,只要有足够的时间和禅宗冥想,几乎所有的 C++ 都可以处理成这种古老的表示形式。这只是需要时间,蚱蜢。

严肃地说,您正在寻找的一般模式似乎是接口和实现。

在大多数情况下,只需复制代码并手动自定义它就可以避免使用模板,但是当您需要处理这样的情况时,通常可以从定义所有重要内容的基类开始。

例如:

class IAddable
{
public:
    virtual IAddable * Add(IAddable * pOther) = 0;
};

class Number
    : public IAddable
{
public:
    virtual IAddable * Add(IAddable * pOther)
    {
        return new Number(this, pOther);
    }

    Number(Number * a, Number * b)
        : m_Value(a->m_Value + b->m_Value)
    { };

private:
    int m_Value;
};
Run Code Online (Sandbox Code Playgroud)

这是一个基本的、不安全的例子,但它说明了这一点。所以是的,你的概念总体上是合理的。

至于使用虚拟操作员,如果这样的事情是可能的(我自己没有使用过),看起来它会起作用。

需要考虑的事情是确保两个操作数可以被操作,为此您可能需要在接口中定义类型检查(类似于 COM 的IUnknown::QueryInterface);

您还必须注意如何处理继承类型;将会涉及到很多指针,因为您和编译器都可能不知道它们是什么。确保经常检查错误和错误类型(返回错误值或转换类型或抛出或你有什么)。