我注意到大多数(全部?)可以存储用户定义类型的C ++数据结构使用模板:
std::vector<T>, std::unordered_map<T....>, etc.
在我看来,它是必需的,否则这些数据结构将必须作为用户必须手动投射的险恶指针void*
返回,如下所示:
std::vector a;
int b;
a.push_back(b);
static_cast<int>(a.get(0));
是否可以不使用模板?还有其他替代方法吗?
修改 许多评论表明,没有模板,这是不可能的/不切实际的。 (谢谢!)
如果我将<T>
限制为真正的指针(例如,不是int / float),那么它仍然不切实际吗?
答案 0 :(得分:4)
是否可以不使用模板?
是。可以定义一般数据结构,允许在不使用模板的情况下对&lt; - 我不知道在何处使用任何类型的动词对象进行分组。
有&#34; C方式&#34; (C中不存在模板)。 C方式是使用void*
。您显然已经熟悉了,但对于答案的其他读者,void*
是一种特殊的指针类型,可以指向任何类型的对象。当您使用void*
来引用某个对象时,您基本上会抛弃该语言提供的所有类型安全性。
我不认为标准库中有任何非模板容器†。但是,如果使用提供的模板实例化std::vector<void*>
或类似的并不是太多,那么您当然可以使用此类模板实例来存储指向任何对象的指针。
模板非常优先于void*
,因为模板不会将类型安全性抛出窗口。模板更容易正确使用。
†还没有。但是计划在C ++ 17中引入std::any
。它可以代替void*
使用。使用std::any
你仍然会抛弃编译时类型的安全性,但至少你保留了运行时的安全性(当你有bug时,你会得到一个异常,而不是潜在的龙)。与void*
不同,std::any
管理存储对象的内存。请注意,虽然std::any
不是模板,但其大多数成员函数都是。
还有其他替代方法吗?
除了模板和void*
?从技术上讲,您也可以使用宏来生成相同程序的不同版本,类似于实例化模板。这有时在C中使用。在C ++中没有理由这样做,因为模板在各方面都更好。
答案 1 :(得分:2)
我将举例说明为什么使用这样的集合会有危险。请考虑以下代码:
#include <vector>
#include <iostream>
using anyvector = std::vector<void *>;
int main() {
anyvector av;
av.push_back(new int(10));
std::cout << (*static_cast<int*>(av[0])) << std::endl;
}
av[0]
是指向使用new
关键字的老式c ++方式创建的int的指针。因此,应该使用delete
关键字释放以这种方式分配的内存。但要做到这一点,应首先将指针强制转换为正确的类型,让编译器知道他真正删除了什么,以防止出现未定义的行为。
要明确我并不是说这是不可能的也不是不切实际的,但它要求程序员全意识......
答案 2 :(得分:2)
您可以依赖类型擦除结构 作为一个极小的,有效的和天真的例子:
#include<memory>
#include<iostream>
#include<vector>
struct Container {
struct B {
virtual void operator()() = 0;
};
template<class T>
struct D: B {
D(T t): o{t} {}
void operator()() override { o(); }
T o;
};
template<typename T>
void add(T t) {
vec.push_back(std::unique_ptr<B>{new D<T>{t}});
}
void operator()() {
for(auto &ref: vec) (*ref)();
}
std::vector<std::unique_ptr<B>> vec;
};
struct P {
void operator()() { std::cout << "P" << std::endl; }
};
struct Q {
void operator()() { std::cout << "Q" << std::endl; }
};
int main() {
Container c;
P p;
Q q;
c.add(p);
c.add(q);
c();
}
C ++ 17可能会引入std::any
并让您轻松完成
在此之前,您可以使用boost::any
或自制类型的擦除类,如上所述。
答案 3 :(得分:2)
我同意当你不能使用容器时,模板是接近容器的正确方法,std::any
(或boost::any
)。然而,仅仅为了有趣的部分,我想指出,通过例外可以实现完美的类型重建。请注意,非常推荐,我只是为了完整性而添加它。在这种情况下,
std::vector<std::function<void()>>
(可以简化),v.push_back([e](){ throw e; });
和catch
例如由{1}抛出的异常。 try{ v[i](); } catch(EType& e) { ... }
。这很难看,不要这样做,这很慢,不要这样做..但是,另一方面,它允许完美的访问(包括继承,你没有得到{{1开放层次结构(你不会用type_info
获得),链接访问者,而不需要基类和&amp;其他虚拟功能。
大胖警告:你可能不会通过这样的代码传递我的CR - 它只是为了有趣和理论上的完整而被提及。