下面是一个示例,我想说明最好使用const_iterator来“const auto”。这是因为容器不提供cfind()函数。还有其他选择吗?或者应该使用“const auto”并忽略const的缺失?
std::string GetJobTitle(const std::string& employee)
{
using EmployeeTitles = std::unordered_map<std::string, std::string>;
EmployeeTitles employees = { { "Alice", "Director" },{ "Bob","Manager" } ,{ "Charlie", "Developer" } };
// Option 1. const_iterator is access only:
EmployeeTitles::const_iterator itr = employees.(employee);
if (itr != employees.cend())
{
itr->second = "Project Manager"; // error C2678: The compiler prevents us changing the job tile which is what we want
return itr->second;
}
// Option 2. const auto is more concise but is not as safe:
const auto& itr2 = employees.find(employee);
if (itr2 != employees.cend())
{
itr2->second = "Project Manager"; // employee now has new title - how can we prevent this with the compiler but still use auto?
return itr2->second;
}
return "";
}
答案 0 :(得分:5)
使用const
变量
您的示例并未说明问题的良好案例。只是对constness更具“攻击性”,它就会消失。您根本不会更改employees
,因此正确的解决方案是首先声明const
:
const EmployeeTitles employees = ...;
这更安全,因为它可以防止在任何地方更改employees
,而不仅仅是通过迭代器进行更改。
使用作用域分隔常量/非常量代码
如果你不能制作employees
const怎么办,因为你只能一块一块地填充它;例如,因为您从数据库中提取信息?将填充代码移动到构建器函数中。或者对于简单的情况,使用立即调用的lambda:
const EmployeeTitles employees = [] {
EmployeeTitles employees;
for (const auto& record : database.employees()) {
// Probably some more processing would be done here in the real world.
employees.emplace(record.name(), record.job_title());
}
return employees;
}();
使用const
成员函数
如果employees
是类的成员变量,并且您在成员函数中迭代它,请使该函数const
。
作为一般规则
每当遇到此问题或类似问题时,请考虑使用const
变量/函数和/或确定范围来完全回避它的方法。这将照顾大多数情况。
在这种情况下,我会选择你的选项1:明确地声明迭代器const_iterator
与地图类型的using
声明相结合。它简洁,易读,立即可以理解,以及表达意图的最直接方式。
操纵employees
常量的其他解决方案并不是那么好,因为这不是你真正关心的。您实际想要的是一个只读迭代器。摆弄employees
的常数只是达到目标的迂回方式。迂回代码更难理解。
另一方面,这并不意味着你会在清晰度上遇到大问题。特别是std::as_const
也很简洁。
但是,对于C ++之前的17代码库,您必须使用const_cast
。这是一个良性的,因为它添加 const,它也不是太冗长。但是我会避免它的一般原则,即在一段代码中看到const_cast
乍一看总是有点吓人。正如@Swift在评论中指出的那样,另一个很好的可能性是实现你自己的as_const
版本。