接受C++"anything"的模板
Templates accepting "anything" in C++
我有一个简单的模板结构体将字符串与值
关联起来template<typename T> struct Field
{
std::string name; T self;
}
我有一个函数,我想接受任何类型的1个或多个字段,并且字段可能是不同的类型,所以我使用std::initializer_list
,因为据我所知,c++缺乏类型化可变参数,无法确定可变参数的大小,并且必须至少有一个其他参数来确定从哪里开始。
问题是我不知道如何告诉它接受可能是不同类型的字段。在Java中,我只会使用foo(Field<?> bar, Field<?>... baz)
,但是c++缺少类型可变参数和通配符。我唯一的另一个想法是将参数设为typestd::initializer_list<Field<void*>>
,但这似乎是一个糟糕的解决方案…有更好的方法吗?
有几件事…
-
c++ 11(你似乎有,因为你正在谈论
std::initializer_list
)确实有类型可变参数,特别是它们被命名为可变模板 -
Java泛型和c++模板是完全不同的东西。Java泛型创建一个单一类型,该类型存储对
Object
的引用,并提供对接口中类型的自动转换,但重要的是它执行类型擦除。
我建议你解释你想要解决的问题,并获得c++惯用的解决方案建议。如果你想真正模仿Java中的行为(我不能坚持Java是一种不同的语言,有不同的习惯用法),你可以在c++中手动使用类型擦除(即使用boost::any
)。但是我很少觉得在程序中需要完全类型擦除……使用变体类型(boost::variant
)比较常见。
如果你的编译器支持可变模板(不是所有的编译器都支持),你总是可以使用它,但是在vector中存储字段对于一个完全泛型的方法来说可能有点复杂,除非你使用类型擦除。(同样,要解决的问题是什么?可能有更简单的解决方案…)
Java泛型更接近于将boost::any
填充到self
变量中,而不是c++模板。试试吧。c++模板创建的类型在默认情况下彼此之间没有运行时或动态关系。
您可以手动引入这种关系,例如通过共同的父类和类型擦除以及明智地使用pImpl
和智能指针。
C类型变参数在c++ 11中过时了。可变模板参数是非常类型安全的,只要你的编译器支持它们(2012年11月的MSVC 2012 CTP支持它们(不是更新1,CTP), clang和非旧版本的gcc也支持它们)。
c++中的模板是一种元编程,更接近于编写一个编写程序的程序,而不是Java泛型。Java泛型有一个共享的"二进制"实现,而c++模板的每个实例是一个完全不同的"程序"(通过类似COMDAT折叠的过程,可以简化为一个二进制实现),其细节由模板代码描述。
template<typename T>
struct Field {
T data;
};
是一个小程序,它说"这里是如何创建字段类型"。当传入int
和double
时,编译器大致做如下操作:
struct Field__int__ {
int data;
};
struct Field__double__ {
double data;
};
,你不会期望这两种类型之间可以转换。
另一方面,Java泛型创建了如下内容:struct Field {
boost::any __data__;
template<typename T>
T __get_data() {
__data__.get<T>();
}
template<typename T>
void __set_data(T& t) {
__data__.set(t);
}
property data; // reading uses __get_data(), writing uses __set_data()
};
其中boost::any
是一个可以容纳任何类型实例的容器,并且对data
字段的访问通过这些访问器进行重定向。
c++提供了使用模板元编程编写与Java泛型等价的东西的方法。要在Java中编写类似c++模板的东西,您必须让Java程序输出自定义Java字节或源代码,然后以允许调试器连接回编写代码的代码的方式运行该代码,作为错误的来源。
在c++模板中不需要使用通配符,因为在c++中它总是知道类型,并且不会像Java中那样被"擦除"。要在c++中编写void foo(Field<?> bar, Field<?>... baz)
方法(或函数),您可以这样写:
template<class T, class... Ts>
void foo(Field<T> bar, Field<Ts>... baz);
每个Field<Ts>
可以是不同的类型。要在函数中使用可变参数,只需使用baz...
。假设你想调用另一个函数:
template<class T, class... Ts>
void foo(Field<T> bar, Field<Ts>... baz)
{
foo2(baz...);
}
你也可以用Field<Ts>...
扩展类型,所以如果你想把它放在一个元组中(你不能把它们放在数组中,因为它们可以是不同的类型):
template<class T, class... Ts>
void foo(Field<T> bar, Field<Ts>... baz)
{
std::tuple<Field<Ts>...> data(baz...);
}
这对于c++来说不是很习惯。也许这是可以做到的;Coplien的书里可能有一些想法。但是c++是强类型的,因为它相信类型;试图把它变成Smalltalk或像野鸡一样折叠可能会导致眼泪。
- 表示"accepting anything for this template argument" C++概念的通配符
- #define 的"Declaration does not declare anything"错误
- 你如何在C++"Press ENTER to continue, anything else to quit"
- 提升精神:如何将"anything from line till particular word"收集到一个变量中?
- 接受C++"anything"的模板
- Do Default Destructors Do Anything?
- "declaration does not declare anything"错误的原因是什么?
- "Declaration does not declare anything"和"expected unqualified-id"