这不是常量自动和冗余?
我在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
你会发现它会导致错误,因为book
是const
。
在我看来这不是明确的代码。 最好在代码中声明x
不会被修改,这样读取代码的其他人将会有正确的期望 。
我将在这里采取不同的angular度。 const
和const_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
有时帮助它优化代码)