【问题标题】:C++ Template Specialization CompilationC++ 模板特化编译
【发布时间】:2010-07-20 16:23:48
【问题描述】:

我将详细概述我的问题以解释我想要实现的目标,如果您想忽略我的问题的细节,问题在最后一段。

我有一个类设计问题,我希望将任何类型的值传递给 push() 和 pop() 函数,这会将传递的值转换为字符串表示形式,该字符串表示形式将附加到类,有效地创建数据流。 pop() 会发生相反的情况,获取流并将流前面的几个字节转换回指定的类型。

将push() 和pop() 模板与stringstream 绑定是一个明显的解决方案。但是,我希望在 DLL 中使用此功能,我可以在其中更改字符串的存储方式(例如加密或压缩),而无需重新编译客户端。如果算法发生变化,则需要重新编译 T 类型的模板。

我的下一个想法是只使用 pushByte()、pushInt()、popByte()、popInt() 等函数。这将允许我在不重新编译客户端的情况下更改实现,因为它们仅依赖于静态界面。这样就好了。但是,它不是那么灵活。例如,如果一个值从一个字节更改为一个短字节,则与该值对应的所有 pushByte() 实例都需要更改为 pushShort(),popByte() 也需要更改为 popShort()。重载 pop() 和 push() 来解决这个问题会导致类型冲突(导致显式转换,最终会导致同样的问题)。

有了上面的想法,我可以创建一个工人阶级。但是,我想知道如何编译专门的模板。如果我创建了push<byte>() 和push<short>(),这将是一个特定类型的重载,并且从字节到短的更改会自动切换使用的模板,这将是理想的。

现在,我的问题是,如果我只使用专门的模板来模拟这种重载(没有 T 类型的模板),是否所有的专门化都可以编译到我的 DLL 中,从而允许我在不重新编译客户端的情况下分派新的实现?还是在客户端编译时以与 T 类型的模板相同的方式选择或删除专用模板?

【问题讨论】:

  • 一些来自 Herb Sutter 的模板专业化和重载的指针。 gotw.ca/gotw/049.htm
  • 为什么重载需要在用户切换类型时显式转换?
  • 如果我将一个short传递给一个用push(byte)、push(short)和push(int)重载的函数,这不会导致从push(short)和push(short)中选择哪个函数的冲突推(int)?一个短的可以传递到任何一个并且是有效的。
  • 没问题,push(short) 是更好的匹配。顺便说一句,您可以对内联代码使用反引号 - 请参阅我的编辑。
  • @Charlie Wilkinson:如果给出了多种可能性,大多数编译器都会选择声明的类型。但是有些(比如我相信的 VS)需要调用者进行显式转换。

标签: c++ dll templates compilation specialization


【解决方案1】:

首先,您不能仅仅拥有没有专门化的基础模板的专门化模板。只是不允许。你必须从一个模板开始,然后你可以提供它的专业化。

您可以在任意一组类型上显式实例化模板,并将所有这些实例化编译到您的 DLL 中,但我不确定这是否真的能为您完成很多工作。归根结底,模板基本上是一种编译时形式的多态性,而您似乎需要(至少是一种有限形式的)运行时多态性。

我可能只是使用重载。我猜你所说的问题是由以下顺序引起的:

int a;
byte b;

a = pop();
b = pop();

您基本上只是在返回类型上重载pop(众所周知,这是不允许的)。我会非常简单地避免这种情况——而不是返回值,而是传递对要修改的值的引用:

int a;
byte b;

pop(a);
pop(b);

这不仅让重载解析起作用,而且至少对我来说看起来也更干净(尽管可能我刚刚写了太多汇编语言,所以我习惯了“流行斧头”之类的东西)。

【讨论】:

  • 如果您想要 const 初始化,您仍然可以提供包装器:template<class T> T pop_it() { T t; pop(t); return t; } ... 或使用虚拟参数:a = pop(int()); :)
【解决方案2】:

听起来你有两个相反的因素:

  1. 您希望您的客户能够推送/弹出/等。每个数字类型。模板似乎是一种自然的解决方案,但这与一致的(只需要编译一次)实现不一致。
  2. 您不希望您的客户在您更改实现方面时必须重新编译。 pimpl idiom 似乎是一个自然的解决方案,但这与通用(适用于任何类型)实现不一致。

根据您的描述,听起来您只关心数字类型,而不是任意 T。您可以在头文件中为每个模板显式声明特化,并在源文件中定义它们,客户端将使用您定义的特化而不是编译自己的特化。特化是编译时多态性的一种形式。现在您可以将它与运行时多态性结合起来——根据与类型无关的实现类来实现特化。您的实现类可以使用boost::variant 来执行此操作,因为您提前知道可能的 T 的范围 (boost::variant<int, short, long, ...>)。如果 boost 不适合你,你可以自己想出一个类似的方案,只要你关心的 T 数量是有限的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-28
    • 2011-07-21
    • 2022-07-05
    • 2018-05-19
    • 1970-01-01
    • 2019-01-17
    • 2020-01-30
    • 1970-01-01
    相关资源
    最近更新 更多