【问题标题】:VC++ allows to use const types for STL containers. Why?VC++ 允许对 STL 容器使用 const 类型。为什么?
【发布时间】:2011-06-23 20:22:25
【问题描述】:

STL 容器要求存储的值是可复制构造和可分配的。 const T 显然不是任何 T 的可分配类型,但我尝试使用它(只是好奇)并发现它可以编译,而且表现得像可分配类型。

vector<const int> v(1);
v[0] = 17;

这在 Visual Studio 2008 中成功运行并将 v[0] 分配给 17。

【问题讨论】:

  • 不仅如此--VS 2010 允许您分配新值v[0]。试试看!
  • 我很好奇 VC 为 typeid(vector::value_type).name() 提供了什么(并将其与 typeid(vector::value_type).name( ) 或 demangle 它)。
  • 一同:int.
  • VC 标准库实现中的 vector::value_type 来自分配器::value_type,它对 const T 进行了部分特化,只是删除了 const:template&lt;class _Ty&gt; struct _Allocator_base&lt;const _Ty&gt;{ typedef _Ty value_type;};

标签: c++ visual-c++ stl constants visual-c++-2008


【解决方案1】:

这不是其他人建议的实现中的错误。

违反 C++ 标准库设施的要求不会使您的程序格式错误,它会产生未定义的行为。

您违反了存储在容器中的值类型必须是可复制构造和可分配的要求(const 类型显然不可分配),因此您的程序表现出未定义的行为。

C++ 标准中适用的语言可以在 C++03 17.4.3.6 [lib.res.on.functions] 中找到:

在某些情况下(替换函数、处理函数、对用于实例化标准库模板组件的类型的操作),C++ 标准库依赖于 C++ 程序提供的组件。如果这些组件不满足其要求,则标准不会对实施提出任何要求。

特别是在以下情况下效果是不确定的:

...

  • 对于在实例化模板组件时用作模板参数的类型,如果对该类型的操作未实现适用要求子条款的语义。

Visual C++ 标准库实现可以对这段代码做任何事情,包括默默地删除或忽略 const 限定,它仍然符合标准。

【讨论】:

  • 在我最卑微的意见中,这里的无声资格删除真的非常丑陋。
  • 资格删除实际上可能会破坏不正当但仍符合标准的代码。您可以编写一个具有 const 限定的复制赋值运算符的类,以确保 t == ut = u 之后的一个非 const 限定的复制赋值运算符不保证后置条件。这样,const T 将是可分配的,但 T 不会。我无法想象任何有效的用例,如果我看到这样的代码,我可能会哭。
  • 由于标准定义了“可赋值”,似乎后置条件是 t 和 u 是等价的。 - 顺便说一句,C++0x 会删除可分配性要求吗?我在草稿中再也看不到它了,不需要分配的操作会起作用是有道理的。
  • @UncleBens:在 C++0x 中,Assignable 和 CopyConstructible 概念要求已被包括 MoveInsertable、CopyInsertable 和 EmplaceConstructible 在内的一组概念取代,并且这些要求不再适用于整个容器,它们适用于容器上的单个操作(因为某些操作仅在对象可复制时才有效,而其他操作仅在对象可移动时才有效)。
【解决方案2】:

这根本不应该工作。正如您所说,在 §23.1 ¶ 3 中指定存储在容器中的对象必须是 CopyConstructible(如 §20.1.3 中所述)和 Assignable

T 类型的 Assignable 要求是 tu 类型为 T,您可以这样做:

t = u

有一个T&amp; 作为返回值,t 相当于u 作为后置条件。 (§23.1 ¶4)

因此,const 类型显然不是 Assignable,因为执行 t = u 会引发编译错误(§7.1.5.1 ¶5)。

我认为这是 Microsoft 实施中的一个错误。如果您甚至尝试实例化vector&lt;const int&gt;,Linux 上的 g++ 会发出典型的 25 kajillion-lines 模板错误(以防万一,在有和没有 -std=c++0x 标志的情况下进行了测试)。

对了,这个IBM FAQ也有详细说明。


理论上,正如@James McNellis 所说,编译器不需要破坏向量实例化(如果它是未定义的行为,任何事情都可能发生 - 包括一切正常);但是,在赋值语句中存在违反标准的行为,应该会产生编译错误。

事实上,operator[] 成员返回一个vector&lt;const int&gt;::reference;该值必须是T 的左值(§23.1 ¶5 表 66);因为T 属于const 类型,所以它将是const 左值。所以我们归结为 (§7.1.5.1 ¶5),它将尝试对 const 元素执行赋值的代码定义为“格式错误”,这需要编译错误或至少是警告,因为赋值-to-const 是可诊断规则(§1.4 ¶1-2)(未指定“无需诊断”语句)。


最终编辑

实际上,@James McNellis 是对的;一旦您通过实例化 vector&lt;const int&gt; 调用了未定义的行为,通常的规则就不再有价值,因此无论它做什么,实现仍然符合标准 - 包括从元素类型中删除 const 或生成通常的鼻恶魔。

【讨论】:

  • 使其工作的更简单方法是使用 remove_const 类型特征(这个确切的特征应该在 0x 中,但可能使用不同的名称)而不是可变的。
  • @Fred Nurk:很可能就像你说的那样。实际上,我认为我写的 mutable 是个脑残,因为 mutable 被放在非 const (§7.1.2 ¶8)类字段上,让它们被 const 方法修改,而这个与从模板类型中删除 const 无关。
  • 这不是实现中的错误。 OP 的程序表现出未定义的行为。
  • @James McNellis:正确,vector 实例化是 UB 并且不需要错误,但分配给 const 引用会。
  • @Matteo:不,它没有。如果您使用不满足复制构造和可分配要求的类型实例化 vector 的行为,则没有任何要求
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 2010-11-30
相关资源
最近更新 更多