std::vector<T>是一种“用户定义类型”吗?

40
当前草案标准的17.6.4.2.1/1和17.6.4.2.1/2中,对由用户注入namespace std的专业化设置了限制。

如果未另有规定,C++程序的行为在向命名空间std或命名空间内部添加声明或定义时是未定义的。程序可以向命名空间std添加任何标准库模板的模板专业化,前提是声明依赖于用户定义类型,并且专业化满足原始模板的标准库要求,且没有被明确禁止。

我找不到标准中定义用户定义类型这一短语的地方。

我听说的一个选择是,不是std::is_fundamental类型的类型是用户定义类型,在这种情况下,std::vector<int>将是用户定义类型

一个替代方案是,所谓的用户定义类型是用户定义的类型。由于用户没有定义std::vector<int>,而且std::vector<int>也不依赖于用户定义的任何类型,因此std::vector<int>不是用户定义类型
这个问题会影响到一个实际问题:“你能否将std::tuple<Ts...>std::hash专门化注入到namespace std中?”能够这样做有些方便--另一种选择是创建另一个命名空间,在其中递归地构建我们对std::tuple(以及可能在std中没有hash支持的其他类型)的哈希,只有当我们在那个命名空间中找不到哈希时,我们才会回退到std
然而,如果这是合法的,那么当标准向namespace std添加std::tuplehash专门化时,已经专门化了它的代码将会失效,从而产生不添加这样的专门化的理由。

虽然我在谈论以std::vector<int>为具体例子,但我想问的是,std中定义的类型是否曾经是用户定义类型。第二个问题是,即使不是,也许当用户使用std::tuple<int>时,它会变成一个用户定义类型(这很棘手:如果std中的某些东西定义了std::tuple<int>,并且您为std::tuple<Ts...>部分特化hash,那会发生什么)。


目前此问题存在未解决的缺陷


1
这并不意味着程序员定义的类型吗?一种在C++规范中尚不存在的类型? - Robert Harvey
5
不,至少从历史上来看不是这样。从历史上看,UDT 是指任何非基础类型,即除枚举和内置类型(如intchar等)之外的任何类型。但我同意在此引用中使用此术语有些奇怪。 - Konrad Rudolph
我有一个想法——也许它源自C标准?但是浏览了一下C草案标准,从数组/struct/等构建的类型集合是一个“派生类型”,而短语“用户定义类型”在标准中并没有出现。 - Yakk - Adam Nevraumont
2
顺便说一句:我已经看到过这种类型的问题几次了,我一直在想为什么质疑“用户定义类型”的定义,而不是简单地质疑17.6.4.2.1中的限制是否需要修复(通过扩展措辞来不仅依赖于UDT)。 - Daniel Frey
@Manu343726 标准库的许多部分,虽然可以由用户实现,但编译器不必以这种方式实现。其中一些无法在没有编译器的额外帮助下由用户实现,例如某些特性类。需要未指定的内在扩展的特性类是否应该是用户定义的类型?std::atomic<int>呢?std::thread呢? - Yakk - Adam Nevraumont
显示剩余5条评论
4个回答

33

斯特劳斯特鲁普教授非常清楚,任何不是内置的类型都是用户定义的。请参见Programming Principles and Practice Using C++第9.1节的第二段。

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

来源

文章明确提到并不是每个人都同意这一点,但我认为这主要是一厢情愿的想法,而不是标准(和斯特劳斯特鲁普教授)实际上所说的,只是某些人想读入其中的东西。


6
标准库提供了许多用户定义的类型。枚举和类被称为“用户定义类型”,因为它们必须由用户定义,而不能像基本类型那样在没有先前声明的情况下使用。 - dyp
1
@dyp 当然,但那只是语言发明者的话和意图,我希望有一个明确的答案。 ;) - Yakk - Adam Nevraumont
@Yakk 对,我以前(和Potatoswatter?)讨论过这个问题,我同意那段话很模糊。我只是想在Daniel的回答中加入更多引用。 - dyp
1
@Yakk 相关:https://dev59.com/kuo6XIcBkEYKwwoYLhPO#EZzknYgBc1ULPQZFcdhE - dyp
1
Stroustrup在使用该术语时,正如Clauses 1-16中所使用的那样,但Clause 17以不同的方式使用它,而且17.6.4.2.1并认为例如std::string是用户定义类型(尽管根据Clauses 1-6,那肯定是一个用户定义类型) - Jonathan Wakely
显示剩余2条评论

18
当第17条款说“用户定义”时,它的意思是“标准中未定义的类型”,因此std::vector<int>std::string不是用户定义的类型,因此您不能专门为std::vector<int>std::vector<std::string>进行特化。另一方面,struct MyClass是用户定义的,因为它不是标准中定义的类型,因此您可以专门为std::vector<MyClass>进行特化。
这与第1-16条款中使用的“用户定义”的含义不同,这种差异令人困惑和愚蠢。有一个defect report关于此事,其中记录了一些讨论,基本上是“是的,库使用了错误的术语,但我们没有更好的术语”。
所以对于你的问题,答案是“取决于情况”。如果你在与C++编译器实现者或核心语言专家交流,std::vector<int>肯定是一个用户定义的类型,但如果你在与标准库实现者交流,则不是。更准确地说,它在17,6.4.2.1方面不是用户定义的。
一种看待这个问题的方法是,标准库在核心语言方面被视为“用户代码”。但标准库对“用户”的定义有所不同,并认为自己是实现的一部分,只有那些不属于库的东西才是“用户定义的”。
编辑:我提议改变库子句使用一个新术语,“程序定义”,意思是在您的程序中定义的内容(而不是标准中定义的UDT,如std::string)。

1
std::vector<MyClass> 依赖于用户定义的类型,因此您还可以为 std::vector<MyClass> 专门化 hash 等。 - Ben Voigt
1
这让我感到难过。不是答案的问题,而是答案很棒。 - Lightness Races in Orbit

8
由于用户不定义std::vector<int>,且std::vector<int>不依赖于用户定义的任何类型,因此std::vector<int>不是用户定义的类型。
逻辑反驳是用户确实定义了std::vector<int>。你会发现std::vector是一个类模板,因此在二进制代码中没有直接的表示形式。从某种意义上说,它通过类型的实例化获得其二进制表示形式,因此声明std::vector<int>对象的行为就是为模板赋予“灵魂”(请原谅我的措辞)。在没有人使用std::vector<int>的程序中,这种数据类型并不存在。
另一方面,按照相同的论点,std::vector<T>不是用户定义的类型,甚至不是类型,它不存在;只有当我们想要(实例化类型)时,它将强制规定结构的布局,但在那之前,我们只能从结构设计属性等方面进行探讨。
注:
上述关于模板不是代码而是......作为代码模板的论点可能有点肤浅,但从Mayer在A. Alexandrescu的书《现代C++设计》中的介绍中得出了逻辑。该相对引用如下:
最终,Andrei将注意力转向基于模板的流行语言习惯和设计模式的实现,特别是GoF[*]模式。这导致与模式社区的一次短暂冲突,因为他们的一个基本原则是模式不能在代码中表示。一旦变得清晰,Andrei正在自动化模式实现的生成而不是尝试编码模式本身,该反对意见就被解除了,我很高兴看到Andrei和GoF之一(John Vlissides)合作撰写了两篇关于Andrei工作的C++ Report专栏文章。

嗯...简单地说,除了注释,你是不是想说"std::vector<T>不是用户定义的类型,因为它根本就不是一个类型(除非T是类型并被实例化)。它是一个模板。",我理解得对吗?然而,任何实例化都将成为UDT。 - luk32

2

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

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

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

需要注意的是,历史用法对于像int *struct foo *void(*)(struct foo****)这样的类型并不清楚。它们是复合类型,但它们(或其中一些)应该被视为用户定义吗?


我试图找到这些地方,但没有成功。哪些地方对比了 fundamentaluser-defined type - Yakk - Adam Nevraumont
@Yakk 在附件中,C.1.2和C.1.8。 - n. m.
尽管您得出了合理的结论,但这并不是第17条款中“用户定义类型”的意思,例如namespace foo { enum bar { }; }被认为是17.6.4.2.1目的下的用户定义类型。 - Jonathan Wakely
2
struct foo*void(*)(struct foo****)不是用户定义的类型,但它们确实依赖于用户定义的类型,这正是第17条款所要求的。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接