【问题标题】:Is std::vector<T> a `user-defined type`?std::vector<T> 是“用户定义的类型”吗?
【发布时间】:2014-07-02 18:40:40
【问题描述】:

current draft standard 的 17.6.4.2.1/1 和 17.6.4.2.1/2 中,对用户注入 namespace std 的特化设置了限制。

C 的行为 ++ 如果程序向命名空间添加声明或定义,则程序未定义 性病 或到一个 命名空间内的命名空间 性病 除非另有规定。程序可以添加模板特化 用于命名空间的任何标准库模板 性病 仅当声明依赖于用户定义类型 并且专业化满足原始模板的标准库要求并且没有明确 禁止。

我找不到在标准中定义短语用户定义类型的位置。

我听说的一个选项是,不是std::is_fundamental 的类型是用户定义的类型,在这种情况下std::vector&lt;int&gt; 将是用户定义的类型。

另一种答案是用户定义的类型是用户定义的类型。由于用户不定义std::vector&lt;int&gt;,并且std::vector&lt;int&gt; 不依赖于用户定义的任何类型,std::vector&lt;int&gt; 不是用户定义的类型

这种影响的一个实际问题是“您能否将 std::hash 的专用化 std::tuple&lt;Ts...&gt; 注入 namespace std?能够这样做有点方便 - 替代方法是创建另一个命名空间,我们递归地构建我们的std::tuple 的散列(可能还有std 中不支持hash 的其他类型),并且当且仅当我们未能在该命名空间中找到散列时,我们才回退到std

但是,如果这是合法的,那么如果标准将 std::tuplehash 特化添加到 namespace std,那么已经特化它的代码将被破坏,从而有理由不在未来。

虽然我在谈论 std::vector&lt;int&gt; 作为一个具体的例子,但我想问一下 std 中定义的类型是否曾经是 用户定义的类型 。第二个问题是,即使不是,std::tuple&lt;int&gt; 可能会在用户使用时成为 用户定义类型(这会变得很棘手:如果 std 内部的某些内容定义了 std::tuple&lt;int&gt;,会发生什么情况,并且您将hash 部分专业化为std::tuple&lt;Ts...&gt;)。


目前有一个open defect解决这个问题。

【问题讨论】:

  • 不是指程序员定义的类型吗? C++ 规范中尚不存在的类型?
  • @Robert 不,至少从历史上看不是。从历史上看,UDT 是任何非基本类型,即除枚举和内置类型以外的任何类型,例如 intchar 等。但我同意这使得这里引用的上下文中的用法很奇怪。
  • 是的,这基本上是我的理解。您可以使用classstruct 定义的任何内容。
  • @Robert 算了,脑子放个屁。我在考虑(非)POD,而不是 UDT。
  • BTW:我已经多次看到这种类型的问题,我一直想知道为什么“用户定义类型”的定义受到质疑,而不是简单地质疑 17.6.4.2.1 中的限制是否简单需要修复(通过扩展措辞以不仅仅依赖 UDT)

标签: c++ c++11 language-lawyer template-specialization c++14


【解决方案1】:

教授。 Stroustrup 非常清楚任何不是内置的类型都是用户定义的。请参阅《C++ 编程原理与实践》中 9.1 节的第二段。

他甚至专门将“标准库类型”称为用户定义类型的示例。换句话说,用户定义的类型是任何复合类型。

Source

文章明确提到并非所有人似乎都同意,但恕我直言,这主要是一厢情愿的想法,而不是标准(和 Stroustrup 教授)实际所说的,只是某些人想要阅读的内容。

【讨论】:

  • TC++PL 第 4 版 (2014),p139 “标准库提供了许多用户定义的类型。” -- 同一页:“枚举和类被称为用户定义的类型,因为它们必须由用户定义,而不是像基本类型那样在没有先前声明的情况下可供使用。”
  • @dyp 当然,但这只是语言发明者的话和意图:我希望得到一些确定的东西。 ;)
  • @Yakk 是的......我之前有过这个讨论(和 Potatoswatter?),我同意这一段是模棱两可的。我只是想在丹尼尔的回答中添加更多引用。
  • Stroustrup 使用条款 1-16 中使用的术语,但条款 17 以不同的方式使用它,并且 17.6.4.2.1 考虑例如std::string 是一个用户定义的类型(即使根据第 1-6 条,它肯定是一个用户定义的类型)
【解决方案2】:

当第 17 条说“用户定义”时,它的意思是“标准中未定义的类型”,所以 std::vector&lt;int&gt; 不是不是用户定义的,std::string 也不是,所以你不能专门化std::vector&lt;int&gt;std::vector&lt;std::string&gt;。另一方面,struct MyClass 是用户定义的,因为它不是标准中定义的类型,所以你可以专门化std::vector&lt;MyClass&gt;

这与第 1-16 条中使用的“用户定义”的含义不同,这种差异令人困惑和愚蠢。对此有一个defect report,记录了一些讨论,基本上说“是的,图书馆使用了错误的术语,但我们没有更好的术语”。

因此,您的问题的答案是“视情况而定”。如果您与 C++ 编译器实现者或核心语言专家交谈,std::vector&lt;int&gt; 绝对是用户定义的类型,但如果您与标准库实现者交谈,则不是。更准确地说,它不是用户定义的17,6.4.2.1

一种看待它的方式是,就核心语言而言,标准库是“用户代码”。但是标准库对“用户”有不同的概念,并认为自己是实现的一部分,只有不属于库的东西才是“用户定义的”。

编辑:我建议更改库子句以使用一个新术语“程序定义”,这意味着在您的程序中定义的东西(与标准中定义的 UDT 不同,例如std::string)。

【讨论】:

  • std::vector&lt;MyClass&gt; 依赖于用户定义的类型,所以你也可以将hash特化为std::vector&lt;MyClass&gt;
  • 这让我很难过。不是答案。答案很好。
【解决方案3】:

由于用户不定义std::vector&lt;int&gt;,并且std::vector&lt;int&gt; 不依赖于用户定义的任何类型,std::vector&lt;int&gt; 不是用户定义的类型。

逻辑上的反驳论点是用户确实定义了std::vector&lt;int&gt;。你看std::vector 是一个类模板,因此在二进制代码中没有直接表示。

从某种意义上说,它通过实例化一个类型来获得二进制表示,因此声明std::vector&lt;int&gt; 对象的行为正是赋予模板“灵魂”的原因(请原谅措辞)。在没有人使用 std::vector&lt;int&gt; 的程序中,此数据类型不存在。

另一方面,遵循相同的论点,std::vector&lt;T&gt; 不是用户定义的类型,它甚至不是类型,它不存在;只有当我们想要(实例化一个类型)时,它将强制结构将如何布局,但在那之前我们只能根据结构来争论它,设计、属性等。

注意

上述论点(关于模板不是代码,而是......代码的模板)可能看起来有点肤浅,但从 Mayer 在 A. Alexandrescu 的书 Modern C++ Design 中的介绍中得出了它的逻辑。那里的相对报价是这样的:

最终,Andrei 将注意力转向了流行语言习语和设计模式的基于模板的实现的开发,尤其是 GoF[*] 模式。这导致了与 Patterns 社区的短暂冲突,因为他们的基本原则之一是模式不能在代码中表示。一旦明确 Andrei 是在自动生成模式实现而不是尝试对模式本身进行编码,反对意见就被消除了,我很高兴看到 Andrei 和其中一位 GoF (John Vlissides) 在两个专栏上合作在专注于 Andrei 工作的 C++ 报告中。

【讨论】:

  • 嗯...只是把它变成简单的英语,除了注释,你说“std::vector&lt;T&gt;不是用户定义的类型,因为它根本不是类型(除非T是类型,它会被实例化)。它是一个模板。”,对吗?但是,任何实例化都是 UDT。
【解决方案4】:

标准草案在几个(非规范)地方将基本类型与用户定义类型进行了对比。

标准草案还在其他上下文中使用术语“用户定义”,指的是由程序员创建的实体在标准库中定义。示例包括用户定义的构造函数、用户定义的运算符和用户定义的转换。

这些事实允许我们在没有其他证据的情况下暂时假设该标准的意图是根据历史用法,用户定义的类型应该意味着复合类型。只有在未来的标准文档中明确说明才能绝对解决问题。

请注意,int*struct foo*void(*)(struct foo****) 等类型的历史用法并不清楚。它们是复合的,但它们(或其中一些)是否应该被认为是用户定义的?

【讨论】:

  • 我试图找到那些点,但找不到。 fundamental用户自定义类型 对比的地方在哪里?
  • @Yakk 在附件中,C.1.2 和 C.1.8。
  • 虽然你得出了一个合理的结论,但这不是第 17 条所说的“用户定义类型”的意思,例如 namespace foo { enum bar { }; } 被认为是 17.6 的用户定义类型.4.2.1
  • struct foo*void(*)(struct foo****) 不是用户定义的类型,但它们确实依赖于用户定义的类型,这是第 17 条所要求的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
  • 2018-02-02
  • 2023-02-16
  • 2020-05-21
  • 2011-09-24
相关资源
最近更新 更多