这不是常量自动和冗余?

我在Stroustrup的书中find了这个代码:

void print_book(const vector<Entry>& book) { for (const auto& x : book) // for "auto" see §1.5 cout << x << '\n'; } 

但是const似乎是多余的,因为x会被推断为一个const_iterator因为book在参数中是const的。 const auto是否真的更好?

在我看来,这看起来很明显 ,这就是为什么它更好 。 所以如果我的意思是const ,那么我更愿意明确写出它。 它增强了本地推理 ,从而帮助其他人比较容易地理解代码 – 其他程序员不必book声明 ,并推断x也是const

代码不仅仅是供编译器阅读 – 也是供人阅读的。 简洁只有在不以可读性为代价时才是好事。

在给定的代码中,推导常量是立即的。 根据你提出的改变,推断常数将要求读者准确地记住或回顾book的定义。

如果读者要意识到不变,简洁就会适得其反。

看到关键字const更清晰。 你马上意识到它不会被修改,如果const关键字不存在,我们就不会知道,除非我们查find函数签名。

显式使用const关键字对于代码读者来说更好。


而且,当你阅读下面的代码时,你期望x是可修改的

 for (auto& x : book) { ... } 

然后当你尝试修改x你会发现它会导致错误,因为bookconst
在我看来这不是明确的代码。 最好在代码中声明x 不会被修改,这样读取代码的其他人将会有正确的期望

我将在这里采取不同的angular度。 constconst_iterator意味着完全不同的东西。

const应用于迭代器意味着你不能修改迭代器本身,但你可以修改它指向的元素,很像一个像int* const声明的指针。 const_iterator意味着你不能修改它指向的元素,但你仍然可以修改迭代器本身(即你可以递增和递减它),就像声明为const int*的指针一样。

 std::vector<int> container = {1, 2, 3, 4, 5}; const std::vector<int> const_container = {6, 7, 8, 9, 0}; auto it1 = container.begin(); const auto it2 = container.begin(); auto it3 = const_container.begin(); const auto it4 = const_container.begin(); *it1 = 10; //legal *it2 = 11; //legal *it3 = 12; //illegal, won't compile *it4 = 13; //illegal, won't compile it1++; //legal it2++; //illegal, won't compile it3++; //legal it4++; //illegal, won't compile 

正如你所看到的,修改迭代器指向的元素的能力只取决于它是一个iterator还是const_iterator ,修改迭代器本身的能力仅仅取决于迭代器的variables声明是否具有const限定符。

编辑:我只是意识到这是一个范围,因此没有迭代器在这里播放(至less不明确)。 x不是一个迭代器,实际上是对容器元素的直接引用。 但是你有正确的想法,无论是否明确写入,都会推导出const限定符。

我想提供一个反例。 在Herb Sutter在CppCon 2014 “回到基础!现代C ++风格的基础” (时间戳34:50)的演讲中,他展示了这个例子:

 void f( const vector<int>& v) { vector<int>::iterator i = v.begin(); // error vector<int>::const_iterator i = v.begin(); // ok + extra thinking auto i = v.begin(); // ok default 

他认为,即使你改变了函数的参数, auto也会正常工作,因为它正确的推导出了const或者non-const 。 进一步在幻灯片36,他说:“你应该知道你的variables是否是const / volatile或不! 作为为什么“auto &&”对于局部variables不好的推理。

基本上,这归结为意见的问题。 有些人认为,明确表示对可读性或可维护性是有好处的,但是相反的观点则可能是:冗余表明缺乏理解(C ++规则或者你的代码实际上正在做什么),并且会损害可读性和可维护性。 做你认为最好的事情。

1 :这是一个颇为人为的例子,但C ++的经验法则在一般情况下并不起作用,而且很难记住。 例如,Herb Sutterbuild议您具有与常规函数重载相反的构造函数parameter passing。 这是其中任何一个地方都可能咬你的脚,取决于你是否同意Herb。

提前提到const ,经常会告诉读者代码更多的内容, 读者不必再远离代码。

现在,给你的代码,我会写它为:

 void print_book(const std::vector<Entry>& book) { for (auto&& x : book) std::cout << x << '\n'; } 

甚至:

 void print_book(std::experimental::array_view<const Entity> book) { for (auto&& x : book) std::cout << x << '\n'; } 

但只是因为代码太短,我会省略多余的const

array_view版本删除对作为vector的参数的无用依赖 – vector<?> const&通常是指定接口,作为vector<?> const&提供没有用的任何有用的array_view<?>不提供,但可以强制从初始化列表或从原始C数组或类似的副本。)

就编译器而言,没有任何理由使用const auto& over auto&auto&& ,因为所有3个(在此上下文中)被推断为是相同的types。 使用其中一个的唯一原因是与其他程序员交谈。

在我的情况下,我默认使用auto&& (它说我正在迭代,并不在乎我正在迭代)。

auto&如果我知道我需要修改,并auto const&如果我想强调我没有修改。

这个具体的例子很简单,因为input数组被声明为const 。 一般来说,在基于范围的情况下使用const对于两个开发人员都是有用的(如果他试图修改容器,他会得到编译器错误),对于编译器(显式const有时帮助它优化代码)