【问题标题】:C++ rules about template equivalence/redefinition?关于模板等效/重新定义的 C++ 规则?
【发布时间】:2019-10-04 01:36:40
【问题描述】:

由于三行,以下 C++ 翻译单元格式错误:

template <int x, int y> struct S {}; // [1]
template <int w, int z> struct S {}; // [2] <-- ill-formed
template <int x> struct S<x,5+2> {}; // [3]
template <int w> struct S<w,3+4> {}; // [4] <-- ill-formed
template <> struct S<6+1,4+3> {}; // [5] 
template <> struct S<2+5,8-1> {}; // [6] <-- ill-formed

问题是[2]是[1]的重定义,[4]是[3]的重定义,[6]是[5]的重定义。

C++ 标准中的哪种特定语言使这种格式不正确?

当两个模板定义定义一个模板的相同特化时(以及它们是模板的两个不同特化时),它在哪里说明?

它在哪里说这是不允许的(重新定义相同的专业化)?

【问题讨论】:

  • 嗯,这不就是标准的 C++ 吗?我的意思是,你也不能在同一个翻译单元中拥有void f() {}void f() {}。您不能在翻译单元中重新定义相同的内容。我看不出其中任何一个的模板性质如何改变。
  • @NicolBolas:是的,这是标准 C++。我知道它的格式不正确。我在问 C++ 标准中的哪些特定规则适用于使这个翻译单元格式错误。答案将包括对标准文本的一个或多个引用。

标签: c++ language-lawyer c++17


【解决方案1】:

我要在这里说“one definition rule”适用于这里(强调他们的和我的):

任何翻译单元不得包含多个变量、函数、类类型、枚举类型或模板的定义。

请注意,模板也属于此规则,而不仅仅是变量。


至于为什么编译器允许这样做,你不是在重新定义模板,而是在专门化它。它与大多数专业并没有太大的不同。这里奇怪的区别是你的专业化是模板化的。

This can be seen 先执行 3,再执行 1:

template <int x> struct S {}; // [3] <-- S takes one parameter now
template <int x, int y> struct S {}; // [1] <-- this is a redefinition, because S only takes one parameter.

如果您问我,这是模板化专业化(接受模板的专业化)和重新定义之间的一条细线,但您的示例在技术上是前者,所以这就是它起作用的原因。


旁注:如果你愿意,你可以随时specialize a vardic template达到同样的效果:

template <typename... T>
struct Foo;

template <typename T1>
struct Foo<T1> {};

template <typename T1, typename T2>
struct Foo<T1,T2> {};

在我看来,这种重新定义和接受模板的专业化之间的界限真的很随意,但这只是我的看法。

【讨论】:

  • 为什么允许 [1] 和 [3] 和 [5]?他们不是同一个模板的专业化吗?还是它们是三个不同的模板?如果是后者,为什么 [1] 和 [2] 是相同的模板,而 [1] 和 [3] 不是?
  • @AndrewTomazos:您是在问“同模板”的概念是如何定义的?或者有一个模板的“定义”意味着什么?
  • @AndrewTomazos 也许是因为,从某种意义上说,当你这样做时,你理论上是specializing a vardic template
  • 更仔细地查看您的代码,这是允许的,因为您每次都专门针对 S,可以说是使用 [1]、[3] 和 [5]。 [2]、[4] 和 [6] 是对这些专业的重新定义。
  • 专业化和重新定义之间的界限肯定不是“武断的”,是吗?如果您想象一个二维网格,ints 在两个轴上,那么 [1] 和 [2] 都覆盖整个网格,[3] 和 [4] 都覆盖同一条线,并且 [5]和 [6] 涵盖了同一点。仅当覆盖的区域相同时才违反 ODR,否则只是专业化。如果一个专业化严格来说更具体/包含在另一个专业化中,则在解析模板时首选它,否则您可能会产生歧义。 (初始定义和特化的区别在于语法。)
【解决方案2】:
template <int x, int y> struct S {}; // [1]
template <int w, int z> struct S {}; // [2] <-- ill-formed

standard, common C++ 的东西禁止这样做。

Names denote entities (or labels, but let's ignore that)。姓名come from declarations。定义是subset of a declaration,所以它也引入了名称。

one-definition rule 说:

任何翻译单元不得包含任何变量、函数、类类型、枚举类型或模板的多个定义。

在语句[1] 中,创建了一个模板实体。该实体由名称 S 表示。

在语句[2] 中,创建了一个模板实体。该实体由名称 S 表示。

这两个名字are the same。如果一个名称表示一个实体(如前所述),那么在适当范围内的相同名称表示同一个实体。因此,两个语句都声明了同一个实体。

ODR 禁止的。


template <> struct S<6+1,4+3> {}; // [5] 
template <> struct S<2+5,8-1> {}; // [6] <-- ill-formed

从语法上讲,S&lt;6+1,4+3&gt;simple-template-id。现在,有人可能会认为这使它成为a class template partial specialization definition。但它没有,因为模板参数为空,即makes this an explicit specialization。即便如此,S&lt;6+1,4+3&gt; 仍然是这个定义的名称。并且明确的专业化定义仍然是定义。

那么问题来了,S&lt;6+1,4+3&gt;S&lt;2+5,8-1&gt; 同名吗?

以下是关于我们如何判断same name is 的规则的摘录:

它们是 template-ids,它们引用相同的类、函数或变量 ([temp.type])

simple-template-id 在语法上是 template-id 的子集。那么这两个 simple-template-id 是否“引用同一个类、函数或变量”?

这由complex set of rules 决定。特别要注意:

它们对应的整数或枚举类型的非类型模板参数具有相同的值

6+12+5 是相同的值。 4+38-1 是相同的值。因此,这两个 simple-template-id 是相同的名称,因此是相同的实体。并且由于这两个定义定义了同一个实体,这违反了 ODR,如前所述。


部分模板特化是另一回事。

template <int x> struct S<x,5+2> {}; // [3]
template <int w> struct S<w,3+4> {}; // [4] <-- ill-formed

前面围绕simple-template-id 的逻辑工作......到了一定程度。部分特化使用 simple-template-id 作为它们的名称,因此它们引入的实体使用相同的规则来测试等价性。

但是,这些规则实际上并没有说明这里会发生什么。 xw 不是值;它们是模板参数。他们的实际价值只有在选择他们的专业时才能知道。而另一个equivalence rules 并没有真正谈论当模板参数是模板参数时会发生什么。

所以技术上,看起来这些定义并不相同。不过……

根据how a template specialization is chosen 的规则,语句[3][4] 引入了entirely ambiguous 的部分特化。看,在特化之间进行选择的规则涉及一组复杂的事物,包括将conversion of your partial specializations 放入一系列函数声明中,并使用template function overloading rules 对其进行排序。所以我没有引用。

归结为您的两种类型的方法是,您尝试实例化的任何 S&lt;value&gt; 都将是模棱两可的。无法从[4] 中检测到[3],因此任何使用其中任何一个的尝试都是错误的。

所以你的编译器基本上是抢先一步,意识到你不可能使用任何一种专业化,并告诉你你的代码被破坏了。

我可能在规范中的某个地方遗漏了一些东西,实际上说名称是相同的,因为它们显然是相同的名称。但是我没找到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 2012-10-13
    • 2011-07-15
    • 1970-01-01
    相关资源
    最近更新 更多