如何将项目放入具有模板化超类

时间:2017-07-11 01:38:59

标签: c++ templates inheritance visual-c++

假设我有以下两个类SuperDeriv,其中DerivSuper的子类。

SuperDeriv都是模板化类。我想创建vector类型Super<?>?表示任何类型的地方。目前我已经想出了这个:

#include <iostream>
#include <vector>

template <typename T>
class Super {
public:
    T val;
    Super(T val) : val(val) {};
};

template <typename T>
class Deriv : public Super<T> {
public:
    Deriv(T val) : Super<T>(val) {};
};

int main() {
    std::vector<Super*> a;
    a.push_back(new Deriv<int>(1));
    a.push_back(new Deriv<float>(1.0f));

    std::cout << a[0]->val << std::endl;
    return 0;
}

当然这不起作用,因为std::vector<Super*>需要像std::vector<Super<int>*>这样的模板类型。但问题是我只能将项目添加到Super<int>*类型的向量而不是Super<float>*

如何更改此代码以允许我将Super类型或其衍生物添加到具有intfloatshort等任何模板类型的向量中等?

4 个答案:

答案 0 :(得分:1)

不确定这是否可以解决您的问题,但它可能会给您一些想法。这里的基本思想是为各种T创建一个超类。对于基础数据类型,需要包装类。

#include <iostream>
#include <cstdio>
#include <vector>
#include <string>

class SuperT {
public:
    virtual std::string AccessData() = 0;
};

class IntWraper : public SuperT {
public:
    IntWraper(int i) : val(i) { };
    virtual std::string AccessData() { return std::to_string(val); };
private:
    int val;
};

class FloatWraper : public SuperT {
public:
    FloatWraper(float f) : val(f) { };
    virtual std::string AccessData() { return std::to_string(val); };
private:
    float val;
};


class RealSuper {
public:
    virtual std::string DoSomething() = 0;
};

template <typename T>
class Super : public RealSuper {
public:
    T* wraper_val_;
    Super(T* w_val) : wraper_val_(w_val) { };
    ~Super() { if(wraper_val_) delete wraper_val_; };
    virtual std::string DoSomething() { return wraper_val_->AccessData(); }
};

template <typename T>
class Deriv : public Super<T> {
public:
    Deriv(T* w_val) : Super<T>(w_val) {};
};

int main() {
    std::vector<RealSuper*> a;
    a.push_back(new Deriv<IntWraper>(new IntWraper(1)));
    a.push_back(new Deriv<FloatWraper>(new FloatWraper(1.0f)));

    std::cout << a[0]->DoSomething() << std::endl;
    std::cout << a[1]->DoSomething() << std::endl;

    return 0;
}

答案 1 :(得分:1)

std::vector<Super*> a;

错了。

代码中没有名为Super的类型。是的,你声明了一个名为“超级”的东西,但它不是一个类,它是一个类模板。

该功能的名称说明了一切。这是一个模板。编译器将使用Super在编译时生成新类型。

例如,Super<int>在填充T模板中的漏洞Super时,请参阅编译器生成的类。

那么为什么a[0]->val无法工作?好吧,想象一下:

template<>
struct Super<std::string> {
    std::string my_val;
};

我们专注于Super,因此在使用std::string进行实例化时,它不再拥有val成员,而是my_val成员。

现在,您希望这段代码做什么?

std::vector<Super*> a;
a.push_back(new Deriv<std::string>);

std::cout << a[0]->val << std::endl;

不是很令人费解吗?您在运行时需要编译错误。由于变量的存在(或不存在)是在编译时确定的,因此不可能。

现在我们如何解决您的问题?

在您的情况下,它就像添加Super之上的接口一样简单,并公开实现计算所需行为的函数:

struct Interface {
    void print(std::ostream) const = 0;
    bool lessThan(double) const = 0;
};

template <typename T>
struct Super : Interface {
    T val;

    Super(T val_) : val{val_} {};

    // We implement the needed behavior.
    void print(std::ostream o) const override {
        o << val << std::endl;
    }

    // Example of calculation
    bool lessThan(double rhs) const override {
        return val < rhs;
    }
};

现在你可以做到:

std::vector<Interface*> a;

// ...

a[0]->print(std::cout);
a[0]->lessThan(3.7);

答案 2 :(得分:0)

创建一个新类RealSuper,缓存该类型是一种可能的解决方法。

这并不完美,但我担心它不会比这更好: -

class RealSuper{                                //<-- new class
    public: enum TYPE{ type_int, type_float, type_notIni };  // (yes, it is awkward)
    TYPE typee = type_notIni;
};
template <typename T>   class Super : public RealSuper { //<-- modify
    public:  T val;
    Super(T val) : val(val) {
        if( std::is_same<T, int>::value ){
            typee = type_int;
        }else if( std::is_same<T, float>::value ){
            typee = type_float;
        }
    };
};
template <typename T>  class Deriv : public Super<T> {
    public: Deriv(T val) : Super<T>(val) {};
};
int main() {
    std::vector<RealSuper*> a;
    a.push_back(new Deriv<int>(1));
    a.push_back(new Deriv<float>(1.0f));
    for(auto ele: a){
        switch( ele->typee ){
            case RealSuper::TYPE::type_int: {
                int value=static_cast<Super<int>*>(ele)->val;
                std::cout << value << std::endl; 
            };break;
            case RealSuper::TYPE::type_float :{
                float value=static_cast<Super<float>*>(ele)->val;
                std::cout << value << std::endl; 
            };break;
        }
    }
    return 0;
}

live demo

这是使用虚函数显示方法的另一个demo

答案 3 :(得分:0)

C ++没有你要求的功能(我相信这是一种具体化)。这意味着您不能在第一个位置存储任意类型,然后在第二个位置的完全不相关的源文件中指定任意操作,然后将操作应用于第三个位置的数据。

您可以解决许多与您要求的问题相近的问题:限制您在第二个位置要执行的操作,限制您在第一个位置存储的类型,以及问题可以解决。或者,限制在第三个位置支持的x类操作对。

请注意,限制操作集的组合也可以起作用。

理论上,您可以在C ++二进制文件中嵌入完整的编译器或解释器,编译代码并动态加载它。 (这基本上是C#/ Java如何管理具体化)。这对大多数问题来说都是不切实际的。该语言不支持此功能,但这是C ++,您可以做任何事情(甚至编写Java / C#编译器)。

由于没有关于您需要解决的潜在问题的信息,我无法告诉您上述哪种方法是正确的。

这就是为什么“如果我有X我想解决Y,但我无法弄明白X.我知道,我只会问堆栈溢出怎么做X!”被称为X / Y问题。我们可以解决Y,但是你问过哪个X甚至没有描述Y就解决了Y.随意发布Y问题并询问它。使用上面的[问一个问题]按钮。

限制您在存储中处理的类型:

存储std::variant。使用std::visit。或者自己编写或使用boost::variant

限制要执行的操作:

使用类型擦除在存储类型时生成每种类型的操作。

在通话点限制操作类型对:

使用RTTI来提取存储的类型,使用大型交换机然后使用解决方案。