const和non-const关键字有何区别?

85

以下两行代码有何不同之处?

map<int, float> map_data;
map<const int, float> map_data;

问题很熟悉,https://dev59.com/5FjUa4cB1Zd3GeqPOR1s - soerium
7个回答

72
  • intconst int是两种不同的类型。

  • std::map<int, float>std::map<const int, float>同样是不同的类型。

std::map<const int, float>std::map<int, float>之间的区别,在某种程度上类似于std::map<int, float>std::map<std::string,float>之间的区别;你会得到每个类型的新地图。

在非const情况下,内部键类型仍然是非const int

std::map<const int, float>::key_type       => const int
std::map<int, float>::key_type             => int

然而,映射键在语义上是不可变的,所有允许直接访问键的映射操作(例如,解引用迭代器,产生value_type)都会将key_type设置为const
std::map<const int, float>::value_type => std::pair<const int, float>
std::map<int, float>::value_type       => std::pair<const int, float>

因此,如果您的实现允许,那么在所有重要方面,差异可能对您来说基本上是看不见的。

然而,并非总是如此:标准正式要求您的键类型可复制和可移动,并且一些实现重用映射节点;在这些实现中,尝试使用const键就会失败。


4
在实际影响方面,你几乎看不出它们之间的区别。但是如果你使用复制/移动键(如libc ++)的stdlib,那么const版本将会出现问题。请参阅http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-July/015926.html以获取相关讨论。 - mitchnull
@mitchnull 嗯,发现得不错。 (顺便说一句)! - Lightness Races in Orbit
@LightnessRacesinOrbit:“标准要求您的密钥类型必须是可复制和可移动的。” 至于可移动性,我在手头的C++标准副本中找不到相关内容,请问能否提供参考或章节号? - beren
啊,谢谢你确认键不能是const。我想让我的键是不可变的,这让我疯了 -_- - Noel Widmer
@NoelWidmer:对于您的地图类型的用户来说,它们已经是不可变的,因此即使将键设置为“const”是可以的,您也不需要这样做。 - Lightness Races in Orbit
显示剩余3条评论

37

已经是const了,在这种情况下写const是多余的。一旦元素被输入,它的key就不能被更改。


编辑:

如评论所述,两行之间确实有不同。例如,如果你编写一个接受map<const int, int>的函数,你无法将map<int, int>传递给它,因为它们是不同的类型

但请注意,虽然它们是不同的类型,但它们的行为相同,因为map中的键已经是const了......

因此,总之......唯一的区别是它们是两种不同的类型,其他的你不需要关心。


18
这不是(完全)正确的。 std::map 的接口将键类型公开为“const”,但这并不意味着两个模板实例化与此答案所暗示的相同。 std::map<const int,float>std::map<int,float>不同的类型 - jrok
1
@jrok是正确的,而这个答案不是。在前一种情况下,key_type实际上仍然是int - Lightness Races in Orbit
6
让这成为一个教训:快不等于完美,点赞不等于正确。 - Lightness Races in Orbit
2
没有人是完美的,我们都会犯错并互相学习。我们在这里学习和帮助 :) - Maroun
2
@LightnessRacesinOrbit 好的,没错!我同意你的观点。顺便说一下,你发了一个好答案。是的,人们应该花时间来发布和接受答案。我自己大多数时候都试图从基本原理上解释,并发布长篇回答(但当然是延迟和低投票的帖子)。无论如何,我祝贺Maroun获得了10K RP。Maroun发布了许多好的答案,我认为他是一个有价值的贡献者。 - Grijesh Chauhan
显示剩余6条评论

9
区别在于第二种变体会将映射的键类型设置为const int。从“修改性”角度来看,这是多余的,因为映射已经将它的键存储为const对象。
然而,这也可能导致这两个映射行为上的意外和不明显的差异。在C++中,针对类型T编写的模板特化与针对类型const T编写的特化不同。这意味着上述两个版本的映射可能最终使用依赖于键类型的各种“卫星”模板的不同专门化。一个例子是键比较器谓词。第一个将使用std::less ,而第二个将使用std::less。通过利用这种差异,您可以轻松地使这些映射以不同的顺序排序其元素。
这样的问题在新的C++11容器(如std::unordered_map)中更加明显。std::unordered_map甚至无法编译,因为它将尝试使用hash该键的std::hash专门化。这样的专门化在标准库中不存在。

3

1
抱歉,我本来想写“不能”,但版主已经修改了。谢谢。 - Shumail

2

尽管应用程序的行为通常是相同的,但对于您可能使用的某些编译器而言,这确实很重要。以下是我最初来到此页面时引起注意的更具体的示例:

显式指定一个map<const key, value>会在gnu工具包中成功构建;

然而,在Studio12 Solaris x86版本中会崩溃。


map<key, value>在两个版本中都可以成功构建。应用程序的行为不变。


“Crashes” 是以什么方式崩溃的? - Lightness Races in Orbit
@LightnessRacesinOrbit 它抱怨 std::map::insert 有多个声明。 - blgt
是的,正如我上面所说的:这对编译器有影响。 - blgt
通常我们所说的“崩溃”,是指进程意外和不优雅地运行终止。编译器崩溃很少见,但确实会发生(特别是在新语言功能方面),并且性质非常严重(因为构建结果受到影响)。 - Lightness Races in Orbit
它使我的构建崩溃,而不是应用程序。我是否误用了术语? - blgt
在我看来,恐怕是这样。 :) 我会说它“破坏”了你的构建,但“崩溃”似乎很具体,不是这里发生的事情。我想这因人而异。 - Lightness Races in Orbit

0

如果键是指针,使用常量键可能会很有帮助。当访问这些键时,使用常量键将不允许您修改被指向的对象,可以考虑以下情况:

#include <map>
#include <string>

int glob = 10;

int main() {
    std::map<const int*, std::string> constKeyMap { { &glob, "foo"} };
    std::map<int*, std::string> keyMap { { &glob, "bar" } };

    for(const auto& kv : keyMap) { *(kv.first) = 20; }; // glob = 20
    for(const auto& kv : constKeyMap) { *(kv.first) = 20; }; // COMPILE ERROR

    return 0;
}

1
key_typeconst int* 时,指针本身不是常量,但所指向的 int 是常量。 - lrineau

-1

const指的是常量,一旦定义后便无法更改...非const键则可能会发生变化...或者根本无法更改,只是在const中“不变”是保证的,而在非const中,“变化”可能发生也可能不发生。


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