简体   繁体   English

非专业模板类型对象的C ++列表?

[英]C++ list of unspecialized template type objects?

I've looked for questions on this subject, and although there are similar ones, none really address my question. 我一直在寻找有关此主题的问题,尽管也有类似的问题,但没有一个能真正解决我的问题。 So, here I go. 所以,我去。

Suppose I have a template type F, and I declare a series of specializations as member variables of another object: 假设我有一个模板类型F,并且我将一系列专门化声明为另一个对象的成员变量:

F<A> a;
F<B> b;
.
.
.

Later, I'd like to iterate over these and perform an operation that is defined on any F instance. 稍后,我想遍历这些对象并执行在任何F实例上定义的操作。

So my impulse is to create a std::list< F> but this does not work. 因此,我的冲动是创建一个std :: list <F>,但这不起作用。

Is there a way to create a list of objects of type F, that is, without specifying the template parameter? 有没有一种方法可以创建类型为F的对象的列表,即不指定template参数呢?

My hunch is no, not in C++, but hoping I am wrong. 我的预感不是,不是C ++,但是希望我错了。

Unfortunately, the answer is NO . 不幸的是,答案是否定的 In C++ you can't have a container of heterogeneous unrelated objects, and different instantiations of the same template are unrelated. 在C ++中,您不能拥有一个异构的不相关对象的容器,并且同一模板的不同实例是不相关的。

The only way to make it work is to use some form of the type erasure. 使其起作用的唯一方法是使用某种形式的类型擦除。 The most idiomatic is to use inheritance and virtual functions, and put pointers to base object into your list. 最惯用的方法是使用继承和虚函数,并将指向基础对象的指针放入列表中。

As far as C++ is concerned, two different template instantiations of a template class or struct are completely separate types and have no relation to each other whatsoever. 就C ++而言,模板类或结构的两个不同模板实例是完全独立的类型,彼此之间没有任何关系。 For example, a std::vector<int> and a std::vector<char> are separate types; 例如, std::vector<int>std::vector<char>是单独的类型; neither derives from the other or from any common base. 两者都不来自对方或任何共同基础。 The fact that both types arose from instantiations of the same template does not create a semantic relationship between the types. 两种类型都来自同一模板的实例化这一事实并没有在这些类型之间创建语义关系。 In that sense (and that sense only ) you might think of templates like preprocessor macros which are expanded before compilation to declare new and unrelated types. 从这种意义上(并且这种意义上),您可能会想到诸如预处理器宏之类的模板,这些模板在编译前已扩展为声明新的无关类型。

But, you can create such a relationship yourself using inheritance, just as you would with any non-template types: 但是,您可以使用继承自行创建这样的关系,就像使用任何非模板类型一样:

#include <iostream>

struct foo_base {
    virtual void print() = 0;
};

template <typename T>
struct foo : foo_base {
    foo(T data) : data(data) {}
    virtual void print() override {
        std::cout << data << std::endl;
    }
    private: T data;
};

void example() {
    foo<int> f(42);
    f.print();
}

Here, a foo<int> and a foo<char> are separate types (as distinct instantiations of the foo<> template), but all foo<> instantiations derive from foo_base . 在这里, foo<int>foo<char>是单独的类型(作为foo<>模板的不同实例化),但是所有foo<>实例化都源自foo_base (Unless, of course, you provide an explicit specialization of foo<> which does not derive from foo_base ...) (当然,除非您提供foo<>的显式特化,它不是foo_base派生的...)

Unless all F<X> derive from some common base-class independent of X , this is only be possible using type erasure or heterogeneous containers. 除非所有F<X>都独立于某个独立于X公共基类派生,否则只有使用类型擦除或异构容器才有可能实现。

If they all inherit from Foo , then of course you can iterate over them as as Foo& / Foo* , but I guess you knew that. 如果它们都继承自Foo ,那么您当然可以将它们作为Foo& / Foo*进行迭代,但是我想您知道这一点。

If they have different types the standard-containers cannot hold them, because they are homogeneous - all elements have the same type. 如果它们的类型不同,则标准容器无法容纳它们,因为它们是同质的-所有元素都具有相同的类型。 There is probably some library with heterogeneous containers, but in the STL, you can only emulate this with std::variant or std::any . 可能有一些带有异构容器的库,但是在STL中,您只能使用std::variantstd::any来模仿。

The last way I could imagine this works out - I don't recommend this - is by creating the enclosing class as a derived class of the F<X> s (variadic template) and encoding the invocations into the hierarchy, st every F s foo` is called 最后一个办法,我能想象这工作了-我不建议这一点-是通过创建封装类的派生类的F<X> S(可变参数模板)和编码调用转换成层次结构,ST每˚F s foo`被称为

Since this is somewhat more involved here is a sample: 由于涉及到更多,这里是一个示例:

#include <iostream>

template <class X>
class Foo {
    X mem;
    public:
    explicit Foo(X arg) : mem(arg) {}
    void foo() {
        std::cout << mem;
    }
};

template <class X, class ...Xs> class Holder;

template <class X>
class Holder<X> {
    X member;
    public:
    Holder(X arg) : member(std::forward<decltype(arg)>(arg)) {}
    void call() {
        member.foo();
    }
};

template <class X, class ...Ys>
class Holder : public Holder<Ys...> {
    X member;
    public:
    Holder(X arg, Ys ...args) : Holder<Ys...>(std::forward<decltype(args)>(args)...), member(std::forward<decltype(arg)>(arg)) { }

    void call() {
        member.foo();
        Holder<Ys...>::call();
    }
};

int main() {
   // omitting the template-args requires c++17 deduction guides
    Holder holder(Foo(4), Foo(" Hello "), Foo(7L), Foo(" World "));
    holder.call();
    std::cout << std::endl;
}

You maybe able to guess, that this is typically not what you want, because it is super-complex. 您也许可以猜测,这通常不是您想要的,因为它非常复杂。 I actually omitted some things like perfectly forwarding the arguments to keep it somewhat minimal, st one can hopefully understand the concept. 实际上,我省略了一些事情,例如完美地传递参数以将其保持在最小限度,希望有人可以理解这个概念。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM