简体   繁体   中英

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<A> a;
F<B> b;
.
.
.

Later, I'd like to iterate over these and perform an operation that is defined on any F instance.

So my impulse is to create a std::list< F> but this does not work.

Is there a way to create a list of objects of type F, that is, without specifying the template parameter?

My hunch is no, not in C++, but hoping I am wrong.

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.

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. For example, a std::vector<int> and a std::vector<char> are separate types; 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 . (Unless, of course, you provide an explicit specialization of foo<> which does not derive from 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.

If they all inherit from Foo , then of course you can iterate over them as as Foo& / Foo* , but I guess you knew that.

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 .

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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