【问题标题】:explicit member specialization显式成员特化
【发布时间】:2011-05-26 17:52:18
【问题描述】:

g++ 3.4.5 接受此代码:

template <typename T> struct A
{
    static const char* const str;
};

struct B {};

typedef A<B> C;

template<> const char* const C::str = "B";
// Equivalent to following?
// template<> const char* const A<B>::str = "B";

但我不确定它实际上是否合法 C++03。特别是,

[14.7p3] 在类模板、类模板的成员或类成员模板的显式特化声明中,显式特化的类的名称应为模板 ID。

这个要求是说在这个例子的最后必须使用非typedef版本吗?还是我误解了什么?

编辑:进一步的证据:Defect Report 403 表明将类型(在这种情况下,函数调用表达式的参数类型)说成是一个 template-id 是不正确的,因为 template-id 有句法意义,而不是语义意义。后来的标准草案在 3.4.2 中使用了“类模板专业化”而不是“template-id”。

这支持了尽管A&lt;B&gt;C 代表相同类型(并且具有相同或几乎相同的语义),但A&lt;B&gt; 是一个模板idC 的论点不是,因为术语 template-id 将句法内容称为一系列标记,而不是这些标记的含义。

【问题讨论】:

  • 不写作为答案,因为我没有时间根据标准来验证它,但我的直觉告诉我 typedef 会为类型生成 alias,而不是新类型.因此,应该可以在使用原始类型的任何地方使用它。请注意,这似乎也被 g++ 接受:struct A { void foo(); }; typedef A B; void B::foo() {} int main() { A a; a.foo(); }
  • @David:关于“任何地方”,一个例外是定义构造函数或析构函数,其中必须使用原始类名。在另一个方向有一个有趣的例外,你不能写(42).~int(),但是当Integerint的typedef时你可以写(42).~Integer()。这与是否属于句法级别有关。而且我猜 OP 问题的症结与句法级别的区别有关。
  • 我会指出 GCC 3.4.5 已经很老了,并且接受了很多不合法的 C++ 内容。并且不接受一些合法的东西。但是 Alf 对 Comeau 的在线评论是比较有力的证据,因为 Comeau 的编译器非常好。
  • @Alf P. Steinbach:谢谢,我不知道。每天你都必须学习新东西……现在我可以回去睡觉了。

标签: c++ templates language-lawyer explicit-specialization


【解决方案1】:

我认为这是严格格式错误的(至少根据 C++ '03)。

我的逻辑是,虽然 typedef (7.1.3/1):

...因此是另一种类型的同义词。

该标准仍然明确允许在需要 class-name 的地方使用 typedef (7.1.3/4):

命名类的 typedef-name 是类名

template-id 没有这样的词。

【讨论】:

  • 我不同意你的看法。 7.1.3/4 旨在不明确允许将 typedef-name 用作类名,但在特殊情况下明确限制 typedef-name 用作类名。同时 7.1.3/1 明确指定 typedef-name 是同义词,如果没有明确限制,可以在任何地方使用。
  • template-idclass-name,参见 [gram.class]。当然,说class-name 不是template-id 是合乎逻辑的,但在这种情况下,我认为这与typedef-name 的定义精神背道而驰。
  • @Stas:如果是这样的话,那么为什么 7.1.3/4 不只是开头:“如果在...之后使用 typedef-name?”?
  • @Steve M:我同意“typedef-name 的精神”。这种事情对于编译器来说可能比仅仅接受代码并继续运行更难捕捉和警告。
  • @Richard Corden:我不知道他们为什么使用这样的措辞。无论如何,我看到了两种指定方式:“全部允许,只禁止特殊情况”或“全部禁止,只允许特殊情况”。我想很明显他们选择了第一种方式。我认为,您会同意几乎不可能禁止所有并明确描述 typedef 的每个允许的情况。
【解决方案2】:

我猜这里没有显式的专业化。不用模板也可以写类似的代码:

struct A
{
    static const char* const str;
};

typedef A C;

const char* const C::str = "B";

即使你使用 C 而不是 A,这段代码也是绝对正确的。C 只是一个别名。

想象一下如何处理 C++ 代码。 Typedef 显然在模板实例化过程开始之前就被编译器扩展了。与所有 cmets 在宏之前展开相同。因为在实际的 C++ 解析开始之前,所有的宏都被预处理器扩展了。这简化了一切。

关于C++03标准,看看那个

7.1.3/1 使用 typedef 说明符声明的名称变为 类型定义名称。在其范围内 声明,typedef-name 是 语法上相当于一个关键字 并命名与 标识符中描述的方式 第 8 条 typedef-name 因此是一个 另一种类型的同义词。一种 typedef-name 没有引入新的 键入类声明的方式(9.1) 或枚举声明。

我猜这一段清楚地解释了你代码中的 C 实际上是一个模板ID。

【讨论】:

  • 这不是一个等价的例子。 “模板 int A::x;”是一个特例,因为 'A' 是被明确专门化的东西。在您的示例中,“C”只是嵌套名称说明符的一部分,并且对其中使用的名称没有此类限制。此外,有些词允许在需要“类名”的地方使用 typedef,因此在您对 7.1.3/1 的引用中,“语法上”并不意味着您可以在任何地方使用 typedef使用类名。
  • @Richard Corden:我不同意你的看法。 7.1.3/4 旨在不明确允许将 typedef-name 用作类名,但在特殊情况下明确限制 typedef-name 用作类名。同时 7.1.3/1 明确指定 typedef-name 是同义词,如果没有明确限制,可以在任何地方使用。
  • 根据我上面的评论:为什么 7.1.3/4 不只是开头:“如果在...之后使用 typedef-name?”?
猜你喜欢
  • 2015-03-04
  • 2011-07-27
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 2018-01-13
相关资源
最近更新 更多