8

我有一个简单的模板结构,将字符串与值相关联

template<typename T> struct Field
{
    std::string name; T self;
}

我有一个函数,我想接受任何类型的 1 个或多个字段,并且这些字段可能具有不同的类型,所以我使用的是std::initializer_list因为 C++,据我所知,缺少类型化的可变参数,无法确定可变参数的大小,并且必须至少有一个其他参数来确定从哪里开始。

问题是我不知道如何告诉它接受可能是不同类型的字段。在 Java 中,我只会使用foo(Field<?> bar, Field<?>... baz),但 C++ 缺少类型化可变参数和通配符。我唯一的另一个想法是制作 type 的参数 std::initializer_list<Field<void*>>,但这似乎是一个糟糕的解决方案......有没有更好的方法来做到这一点?

4

4 回答 4

9

有几件事...

  • C ++ 11(您似乎在谈论std::initializer_list)确实具有类型化的可变参数,特别是它们被命名为可变参数模板

  • Java 泛型和 C++ 模板是完全不同的野兽。Java 泛型创建一个单一类型,该类型存储对接口中类型的引用Object并提供自动强制转换,但重要的是它执行类型擦除。

我建议您解释您想要解决的问题,并获得 C++ 中惯用的问题解决方案的建议。如果您想真正模仿 Java 中的行为(我不能坚持认为它是一种不同的语言并且具有不同的习语),您可以在 C++ 中手动使用类型擦除(即 use boost::any)。但我很少觉得需要在程序中进行全类型擦除……使用变体类型 ( boost::variant) 更为常见。

如果您的编译器支持可变参数模板(并非所有编译器都支持),您总是可以使用它,但是除非您使用类型擦除,否则对于完全通用的方法而言,将字段存储在向量中以供稍后使用可能会有点复杂。(再次,要解决什么问题?可能有更简单的解决方案......)

于 2013-02-28T04:47:17.543 回答
4

与 C++ 模板相比,Java 泛型更接近于将 a 填充boost::any到变量中。self试试看。默认情况下,C++ 模板创建的类型彼此之间没有运行时或动态关系。

您可以手动引入这种关系,例如通过共同的父级和类型擦除以及明智地使用pImpl和智能指针。

C 类型的可变参数在 C++11 中已经过时了。可变模板参数是非常类型安全的,只要您的编译器支持它们(MSVC 2012 的 2012 年 11 月 CTP 支持它们(不是更新 1,CTP),clang 和非古代版本的 gcc 也是如此)。

C++ 中的模板是一种元编程,更接近于编写程序来编写程序,而不是 Java 泛型。Java Generic 有一个共享的“二进制”实现,而 C++ 模板的每个实例都是一个完全不同的“程序”(通过 COMDAT 折叠之类的过程,可以将其简化为一个二进制实现),其详细信息由模板描述代码。

 template<typename T>
 struct Field {
   T data;
 };

是一个小程序,上面写着“这里是如何创建字段类型”。当你传入一个intanddouble时,编译器会做大致这样的事情:

 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()
};

whereboost::any是一个容器,它可以容纳任何类型的实例,并且data通过这些访问器对字段的访问重定向。

C++ 提供了使用模板元编程编写与 Java 泛型等效的方法的方法。要在 Java 中编写诸如 C++ 模板之类的东西,您必须让 Java 程序输出自定义 Java 字节或源代码,然后以允许调试器连接回将代码编写为源代码的方式运行该代码的错误。

于 2013-02-28T04:09:11.883 回答
2

在 C++ 模板中不需要使用通配符,因为在 C++ 中它总是知道类型,并且不像在 Java 中那样被“擦除”。要void foo(Field<?> bar, Field<?>... baz)在 C++ 中编写方法(或函数),您可以编写:

 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...);
 }
于 2013-02-28T15:23:35.883 回答
-1

这对于 C++ 来说不是很惯用。也许可以做到;Coplien 的书可能有一些想法。但是 C++ 是强类型的,因为它相信类型;试图把它变成 Smalltalk 或像野鸡一样折叠它可能会导致流泪。

于 2013-02-28T03:59:03.903 回答