标准库/模板化容器的常量语义的经验法则

Rule-of-thumb for const semantics for standard library/templated containers?

本文关键字:常量 语义 经验法则 标准      更新时间:2023-10-16

>当有人写了

template <typename T> class Container;
当你

有一个const Container<const MyType>时会发生什么就很清楚了(好吧,假设你知道常量对MyType意味着什么(。你不能改变任何东西 - 没有重新排序结构,没有添加或删除元素,不能改变现有元素;如果引擎盖下确实发生了变化(例如 mutable(你应该另眼相看,因为它不算数。

你所说的Container<MyType>是什么意思也很清楚 - 一切都是可变的(可变类中的常量成员除外等(

当你将恒常性与非恒常性混合在一起时,事情就会变得混乱。有了const Container<MyType> - 期望修改MyType元素是否合理?如果容器的状态取决于它们的值怎么办?另外,如果我有

using Stuff = Container<MyType>;

然后我传递const Stuff参数。这种不透明的措辞让你质疑你是否"应该"改变 Stuff 元素中的任何内容;毕竟,你不能说"深const Stuff,不要碰里面的任何东西"。没有人喜欢写作:

using ConstStuff = Container<const MyType>;

然后把尴尬的const ConstStuff传来传去。然而,标准容器完全(?(能够为自己生活const,但不能为他们的元素生活。

甚至Container<const MyType>的语义也会带来一定的语义问题:您可以删除容器中的项目;并且可能需要能够复制它们。这并不能构成非常const的 y 元素。

最后,当您有多个模板化类型参数时,事情会变得更加混乱。假设现在是

template <typename K, typename V> class Container;

(是的,这就像一个std::map,这是这个问题的动机。有了这个,你可以拥有一个恒定的容器但可变的键 - 荒谬,对吧?你可以通过改变它们来完全搞砸它。或者,假设它是一个具有const键但非const值的容器,并假设它通过不保留相同值的多个副本而是指向该值的单个副本来最小化存储。然后你来改变这个值。

在这些情况下,关于 const 的语义是否有某种约定或经验法则?

注意:我特别忽略了指针,您也可以在答案中忽略它们(如果您愿意,也可以忽略它们(。

这个问题

更适合 programmers.stackexchange.com,但没关系。

如果引擎盖下确实发生了变化(例如可变(,您应该另辟蹊径,因为它不算数。

不,有些事情可能会合法地改变。 因为这个const Container可能只是一个你特别不能修改的接口,而实现它的人可能为自己保留了随意修改它的自由,这完全没问题。 将这const Container交给您的人可能这样做是为了不必实例化新容器,也不必制作自己的非常量容器内容的安全副本。

当你将恒常性与非恒常性混合在一起时,事情就会变得混乱。使用 const 容器 - 期望修改 MyType 元素是否合理?

是的。 这就是为什么它是const Container<MyType>而不是const Container<const MyType>. 完全合理。

如果容器的状态取决于它们的值怎么办?

好吧,那时事情会变得混乱。但这真的很奇怪。 (这就是非常奇怪的事情通常发生的事情:它们开始变得混乱。

甚至 Container 的语义也会带来一定的语义问题:您可以删除容器中的项目;并且您可能需要能够复制它们。这并不能构成非常温和的元素。

不,这些元素是完全 consty 的,因为通过从 const 对象复制来初始化实例正是复制构造函数所做的,并且因为从容器中删除对象并不意味着需要对对象执行任何操作,即使对象由于从容器中删除而被破坏, 调用 const 对象的析构函数是完全可以的。

最后,当您有多个模板化类型参数时,事情会变得更加混乱。假设现在它是template <typename K, typename V> class Container;(是的,它就像一个std::map,这是这个问题的动机。有了这个,你可以拥有一个恒定的容器但可变的键 - 荒谬,对吧?

是的,那有点荒谬。 但是语言必须为您提供声明具有不可变键和可变值的容器的能力,因此该语言必须提供完成此类操作所需的功能。

除了

密切关注恒常性之外,我不知道该提出什么建议。