不同类型的模板类列表

fon*_*onZ 5 c++ templates

我正在尝试列出变量类型的模板类。所以这个想法是循环一个对象列表,这些对象都有一个共同的函数,例如getValue,但类型不同。类型可以是任何类型、原始类型或对象。

我需要这个是因为我想要一个类,其中包含我希望能够在运行时构造的不同类型的属性列表。

所以我的课程看起来像:

class MyClass {
    std::list<Attribute<?>*> attributes;
};
Run Code Online (Sandbox Code Playgroud)

还有我的属性模板:

template<typename T>
class Attribute {
public:
    Test(const T &t) : _t(t) {}

    T getValue() const { return _t; }
    void setValue(const T &t) { _t = t; }

private:
    T _t;
};

int main() {
    MyClass myClass;
    myClass.attributes.push_back(new Attribute<int>(42));
    myClass.attributes.push_back(new Attribute<double>(42.0));
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的我放的 MyClass 列表?因为那是我的问题。我不知道如何制作一个列表,该列表将采用不同类型的属性模板,即 int、double 等。

std::list<Attribute<?> *> attributes;
Run Code Online (Sandbox Code Playgroud)

在 Java 中,可以使用泛型。在 C++ 中是否可以通过某种构造来做到这一点?我尝试使用可变参数模板,但这似乎无助于解决我的问题。

我需要这个,但不是在 Java 中,在 C++ 中:

public class GenericAttribute<T> {

    private T value;

    public GenericAttribute (T value) {
        setValue(value);
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

public static void main(String[] args) {

    class Custom {
        public Custom() {}

        @Override public String toString() {
            return "My custom object";
        }
    }

    List<GenericAttribute<?>> attributes = new ArrayList<GenericAttribute<?>>();
    attributes.add(new GenericAttribute<Integer>(1));
    attributes.add(new GenericAttribute<Double>(3.1415926535));
    attributes.add(new GenericAttribute<Custom>(new Custom()));

    for (GenericAttribute<?> attr : attributes) {
        System.out.println(attr.getValue());
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

1
3.1415926535
My custom object
Run Code Online (Sandbox Code Playgroud)

谢谢您的帮助!

fir*_*rda 9

版本 3:非常高级(不要在家里尝试:D)

class Attribute {
private:
    struct Head {
        virtual ~Head() {}
        virtual void *copy() = 0;
        const type_info& type;
        Head(const type_info& type): type(type) {}
        void *data() { return this + 1; }
    };
    template <class T> struct THead: public Head {
        THead(): Head(typeid(T)) {}
        virtual ~THead() override { ((T*)data())->~T(); }
        virtual void *copy() override {
            return new(new(malloc(sizeof(Head) + sizeof(T)))
                THead() + 1) T(*(const T*)data()); }
    };
    void *data;
    Head *head() const { return (Head*)data - 1; }
    void *copy() const { return data ? head()->copy() : nullptr; }
public:
    Attribute(): data(nullptr) {}
    Attribute(const Attribute& src): data(src.copy()) {}
    Attribute(Attribute&& src): data(src.data) { src.data = nullptr; }
    template <class T> Attribute(const T& src): data(
      new(new(malloc(sizeof(Head) + sizeof(T))) THead<T>() + 1) T(src)) {}
    ~Attribute() {
        if(!data) return;
        Head* head = this->head();
        head->~Head(); free(head); }
    bool empty() const {
        return data == nullptr; }
    const type_info& type() const {
        assert(data);
        return ((Head*)data - 1)->type; }
    template <class T>
      T& value() {
        if (!data || type() != typeid(T))
            throw bad_cast();
        return *(T*)data; }
    template <class T>
      const T& value() const {
        if (!data || type() != typeid(T))
            throw bad_cast();
        return *(T*)data; }
    template <class T>
      void setValue(const T& it) {
        if(!data)
            data = new(new(malloc(sizeof(Head) + sizeof(T)))
                THead<T>() + 1) T(it);
        else {
            if (type() != typeid(T)) throw bad_cast();
            *(T*)data = it; }}
public:
    static void test_me() {
        vector<Attribute> list;
        list.push_back(Attribute(1));
        list.push_back(3.14);
        list.push_back(string("hello world"));
        list[1].value<double>() = 3.141592;
        list.push_back(Attribute());
        list[3].setValue(1.23f);
        for (auto& a : list) {
            cout << "type = " << a.type().name()
              << " value = ";
            if(a.type() == typeid(int)) cout << a.value<int>();
            else if (a.type() == typeid(double)) cout << a.value<double>();
            else if (a.type() == typeid(string)) cout << a.value<string>();
            else if (a.type() == typeid(float))  cout << a.value<float>();
            cout << endl;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

输出:

type = i value = 1
type = d value = 3.14159
type = Ss value = hello world
type = f value = 1.23
Run Code Online (Sandbox Code Playgroud)

解释:

Attribute包含data指针,它由这个奇怪的 位置 new初始化:new(new(malloc(sizeof(Head) + sizeof(T))) THead<T>() + 1) T(src)它首先分配足够的空间Head 2*sizeof(void*)对于任何架构的任何对齐都应该是合适的)和类型本身,构造THead<T>() (初始化指向虚拟方法表和类型信息的指针)并将指针移动到 head = 之后我们想要数据的位置。然后使用 copy-constructor (或 move-constructor) 由另一个放置 new 构造该对象T(src)struct Head有两个虚函数——析构函数和copy()THead<T>Attribute(const Attribute&)复制构造函数中实现和使用。最后~Attribute()析构函数调用~Head()虚拟析构函数并释放内存(如果数据!= nullptr)

版本 1:简单属性列表

#include <vector>
#include <typeinfo>
#include <iostream>
#include <cstdlib>
#include <new>

using namespace std;

class Attributes {
public:
    typedef pair<const type_info&,void*> value_type;
    typedef vector<value_type> vect;
    typedef vect::const_iterator const_iterator;
    template <class T>
      void add(const T& value) {
        data.push_back(pair<const type_info&,void*>(
          typeid(T), new(malloc(sizeof(T))) T(value))); }
    const_iterator begin() const {
        return data.begin(); }
    const_iterator end() const {
        return data.end(); }
private:
    vect data;
} attrs;

int main() {
    attrs.add(1);
    attrs.add(3.14);
    for (auto a : attrs) {
        cout << a.first.name() << " = ";
        if(a.first == typeid(int))
            cout << *(int*)a.second;
        else if(a.first == typeid(double))
            cout << *(double*)a.second;
        cout << endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

i = 1
d = 3.14
Run Code Online (Sandbox Code Playgroud)

版本 2(命名属性):

#include <string>
#include <unordered_map>
#include <typeinfo>
#include <iostream>
#include <cstdlib>
#include <new>

using namespace std;

class Attributes {
public:
    typedef pair<const type_info&,void*> value_type;
    typedef unordered_map<string,value_type> map;
    typedef map::const_iterator const_iterator;
    template <class T>
      bool add(const string& name, const T& value) {
        auto it = data.insert(make_pair(
          name, value_type(typeid(T), nullptr)));
        if (!it.second) return false;
        it.first->second.second = new(malloc(sizeof(T))) T(value);
        return true; }
    template <class T>
      const T& get(const string& name) const {
        auto it = data.at(name);
        if (it.first != typeid(T)) throw bad_cast();
        return *(T*)it.second; }
    const_iterator begin() const {
        return data.begin(); }
    const_iterator end() const {
        return data.end(); }
    void erase(const_iterator it) {
        free(it->second.second);
        data.erase(it); }
    bool remove(const string& name) {
        auto it = data.find(name);
        if (it == data.end()) return false;
        free(it->second.second);
        data.erase(it);
        return true; }
private:
    map data;
} attrs;

int main() {
    attrs.add("one", 1);
    attrs.add("pi", 3.14);
    cout << "pi = " << attrs.get<double>("pi") << endl;
    attrs.remove("pi");
    for (auto a : attrs) {
        cout << a.first << " = ";
        if(a.second.first == typeid(int))
            cout << *(int*)a.second.second;
        else if(a.second.first == typeid(double))
            cout << *(double*)a.second.second;
        cout << endl;
    }
}
Run Code Online (Sandbox Code Playgroud)