什么时候应该使用const_iterator而不是auto

R S*_*ath 6 c++ c++11

下面是一个例子,我想说明最好使用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 "";
}
Run Code Online (Sandbox Code Playgroud)

bes*_*esc 5

如果可能的话可以回避问题

使用const变量

您的示例并未说明问题的良好案例.只是对constness更具"攻击性",它就会消失.你根本没有改变employees,所以正确的解决方案是首先声明它const:

const EmployeeTitles employees = ...;
Run Code Online (Sandbox Code Playgroud)

这甚至更安全,因为它可以防止对employees任何地方的更改,而不仅仅是通过迭代器进行更改.

使用作用域来分隔const /非const代码

如果你不能制作employeesconst 怎么办,因为你只能一块一块地填充它; 例如,因为您从数据库中提取信息?将填充代码移动到构建器函数中.或者对于简单的情况,使用立即调用的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;
}();
Run Code Online (Sandbox Code Playgroud)

使用const成员函数

如果employees是类的成员变量,并在成员函数中迭代它,请创建该函数const.

作为基本规则

无论何时遇到这个问题或类似问题,都要想办法使用const变量/函数和/或确定范围来完全回避它.这将照顾大多数情况.

如果你有什么碰上呢?

在这种情况下,我会选择你的选项1:明确地声明迭代器const_iteratorusing地图类型的声明相结合.它简洁,易读,易于理解,是表达意图的最直接方式.

其他操纵常量的解决方案employees并不是那么好,因为这不是你真正关心的.你真正想要的是一个只读迭代器.摆弄常数employees只是达到目标的迂回方式.并且环形交叉代码更难理解.

另一方面,这并不意味着你会在清晰度上遇到大问题.特别std::as_const是很好,也很简洁.

但是,使用前C++ 17代码库,您必须使用const_cast.这是一个良性的,因为它添加了 const,它也不是太冗长.但是我会避免它的一般原则,即const_cast在第一眼看到一段代码总是有点可怕.正如@Swift在评论中指出的那样,另一个很好的可能性就是实现你自己的版本as_const.