我一直在寻找有关此主题的问题,尽管也有类似的问题,但没有一个能真正解决我的问题。所以,我去。
假设我有一个模板类型F,并且我声明了一系列专门化作为另一个对象的成员变量:
F<A> a;
F<B> b;
.
.
.
稍后,我想遍历这些对象并执行在任何F实例上定义的操作。
所以我的冲动是创建一个std :: list
是否有一种方法可以创建类型为F的对象的列表,即无需指定模板参数?
我的直觉不是,不是C ++,但希望我错了。
答案 0 :(得分:2)
不幸的是,答案是 NO 。在C ++中,不能有一个异构的不相关对象的容器,并且同一模板的不同实例是不相关的。
使其生效的唯一方法是使用某种形式的类型擦除。最惯用的是使用继承和虚拟函数,并将指向基础对象的指针放入列表中。
答案 1 :(得分:2)
就C ++而言,模板类或结构的两个不同模板实例是完全独立的类型,彼此之间没有任何关系。例如,std::vector<int>
和std::vector<char>
是分开的类型;两者都不来自对方或任何共同基础。两种类型都来自同一模板的实例化这一事实并没有在这些类型之间创建语义关系。从这种意义上(并且仅是 ),您可能会想到诸如预处理器宏之类的模板,这些模板在编译前会进行扩展以声明新的无关类型。
但是,您可以使用继承自己创建这样的关系,就像使用任何非模板类型一样:
#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();
}
在这里,foo<int>
和foo<char>
是单独的类型(作为foo<>
模板的不同实例化),但是所有foo<>
实例化都源自foo_base
。 (当然,除非您提供foo<>
的显式专业化,而不是不是从foo_base
派生的。)
答案 2 :(得分:2)
除非所有F<X>
都衍生自一些独立于X
的公共基类,否则只能使用类型擦除或异构容器来实现。
如果它们都继承自Foo
,那么您当然可以将它们迭代为Foo&
/ Foo*
,但我想您知道这一点。
如果它们的类型不同,则标准容器无法容纳它们,因为它们是同质的-所有元素都具有相同的类型。可能有一些带有异构容器的库,但是在STL中,您只能使用std::variant
或std::any
来模拟。
我可以想象得到的最后一种方法-我不建议这样做-是通过将封闭类创建为F<X>
(派生模板)的派生类并将调用编码到层次结构中,圣每个F s
foo`都被称为
由于涉及到更多,这里是一个示例:
#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;
}
您也许可以猜测,这通常不是您想要的,因为它非常复杂。我实际上省略了一些事情,例如完美地转发参数以使其保持最小程度。希望可以理解这个概念。