【问题标题】:Pure Virtual member function with compile-time known parameter?具有编译时已知参数的纯虚拟成员函数?
【发布时间】:2019-07-13 11:44:36
【问题描述】:

我正在通过尝试为 STL 容器实现类型擦除模式来练习类型擦除模式,但我坚持使用这些容器的纯虚拟成员函数。我不知道如何实现类型擦除模式的“概念”,它充当接口,持有被擦除类型共享的纯虚成员函数。 Push 之类的函数将需要一个编译时已知参数。据我了解,虚函数不能使用自动或模板化,那我该如何编写接口呢?

我尝试使用关键字'typename'告诉编译器稍后会给出类型,但它没有编译。

这是我目前所拥有的“概念”界面:

class Concept{
public:
    virtual void push(typename T val) = 0;
    virtual typename T pop() = 0;
};

目前收到的错误是这样的:

error: expected nested-name-specifier before ‘T’ virtual void push(typename T val) = 0;
                              ^
error: expected ‘,’ or ‘...’ before ‘val’ virtual void push(typename T val) = 0;
                                ^~~
error: expected nested-name-specifier before ‘T’ virtual typename T pop() = 0;

如果有人能就此给我一些建议,我将不胜感激。提前感谢大家的热心帮助和时间。

【问题讨论】:

  • 如果结构/类本身是一个模板,没有什么能阻止虚函数使用模板类型。你只是不能让虚函数本身成为模板。

标签: c++ type-erasure pure-virtual


【解决方案1】:

typename 关键字只能是模板声明的一部分。

template <typename T> class Concept{
public:
    virtual void push(T val) = 0;
    virtual T pop() = 0;
};

您在脑海中混合了模板和纯虚函数。第一个是编译时间,第二个是运行时间

模板允许您避免为不同的数据类型重复代码,其中纯虚成员函数允许您使用从同一类型继承的不同多态接口。 Type erasure 也与虚成员函数无关。两种完全不同的东西。

一旦上面的模板被实例化为int,那么它就等于这个:

   class Concept{
    public:
        virtual void push(int val) = 0;
        virtual int pop() = 0;
    };

现在这个类是抽象的;你不能实例化它,但你可以继承:

class f1 : public Concept {
    public:
    virtual void push(int val) { ... define it }
    virtual int pop()  { ... define it}
};

class f2 : public Concept {
    public:
    virtual void push(int val) { ... define it }
    virtual int pop()  { ... define it}
    ... more members
};

并以多态方式使用它:

Concept* a = new f1();
Concept* b = new f2();

// dynamic_cast<f1>(a) will return a f1*
// dynamic_cast<f2>(b) will return a f2*

【讨论】:

  • 感谢您的回复,很抱歉造成混乱。这让我更清楚了。我试图将我的概念类转换为模板版本,但遇到了不同的问题。我的容器类将指向概念的指针存储为私有成员变量,但现在该概念已模板化,如果不模板化容器类,我将无法存储指向概念的指针。也是模板化容器的唯一方法吗?
  • 看你要不要使用模板?
  • 我希望容器不是模板类。这可能吗?
  • 然后在其中放入概念的特化,(例如,Concept x),或者,使用 std::any 在运行时区分包含的项目.
  • 非常感谢您的回答,他们帮助我更好地理解了事物。我使用 std::any 来实现我的目标。
【解决方案2】:

我不认为你真的想要一个接口,其中包含无限数量的带有不同参数的 push 和 pop 方法。我也不认为您希望有许多不同的概念后代实现以及每种类型的单独堆栈。 似乎您想推送到不同类型的堆栈对象,然后弹出它们。在这种情况下,它可能是这样的:

struct Container 
{
    template<typename T>
    Container(T t);
    template<typename U>
    U cast();
};

class Stack
{
public:
    virtual void push(Container val) = 0;
    virtual Container pop() = 0;
};

class ConcreteStack : public Stack
{
public:
    void push(Container val);
    Container pop();
};


int main() 
{
    ConcreteStack stack;
    stack.push(25);
    stack.push(std::string("abcd"));

    std::string str = stack.pop().cast<std::string>();
    int num = stack.pop().cast<int>();
}

不幸的是,我看不到在弹出时逃避显式类型转换的方法。同名的方法不能只在返回类型上有所不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-20
    • 2010-09-21
    • 1970-01-01
    • 2016-04-19
    相关资源
    最近更新 更多