Frage Wann sollte const_iterator anstelle von auto verwendet werden?


Unten ist ein Beispiel, das einen Fall veranschaulicht, in dem es vorzuziehen ist, const_iterator zu "const auto" zu verwenden. Dies liegt daran, dass Container keine cfind () - Funktion bereitstellen. Gibt es eine Alternative? Oder sollte "const auto" verwendet werden und das Fehlen von const ignoriert werden?

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 "";
}

6
2018-05-04 01:16


Ursprung


Antworten:


Wenn möglich, umgehen Sie das Problem

Benutzen const Variablen

Ihr Beispiel zeigt keinen guten Fall für das Problem. Sei einfach "aggressiver" mit der Konstanz und es verschwindet. Du änderst dich nicht employees überhaupt, so ist die richtige Lösung, es zu erklären const an erster Stelle:

const EmployeeTitles employees = ...;

Das ist noch sicherer, weil Änderungen verhindert werden employees überall, nicht nur durch den Iterator verändert.

Verwenden Sie das Scoping, um const / nicht-const-Code zu trennen

Was, wenn Sie nicht machen können employees const, weil man es nur Stück für Stück bevölkern kann; Zum Beispiel, weil Sie die Informationen aus einer Datenbank ziehen? Verschieben Sie den Populationscode in eine Builder-Funktion. Oder verwenden Sie für einfache Fälle ein sofort aufgerufenes 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;
}();

Benutzen const Mitgliedsfunktionen

Ob employees Ist eine Membervariable einer Klasse und Sie durchlaufen sie in einer Memberfunktion, machen Sie diese Funktion const.

Generell

Wann immer Sie auf dieses Problem oder etwas Ähnliches stoßen, denken Sie darüber nach, wie Sie es verwenden können const Variablen / Funktionen und / oder Scoping, um alles zu umgehen. Das wird sich um die meisten Fälle kümmern.

Was wenn du machen hineinlaufen?

In diesem Fall würde ich mit Ihrer Option 1 gehen: Erklären Sie den Iterator explizit const_iterator in Kombination mit der using Deklaration für den Kartentyp. Es ist prägnant, lesbar, sofort verständlich und der direkteste Weg, um Ihre Absicht auszudrücken.

Andere Lösungen, die die Konstanz von employees sind nicht so gut, weil es dir nicht wirklich wichtig ist. Was du tatsächlich want ist ein schreibgeschützter Iterator. An der Konstanz von employees ist nur ein Umweg, um dieses Ziel zu erreichen. Und Umgehungscode ist schwerer zu verstehen.

Auf der anderen Seite bedeutet das nicht, dass Sie große Probleme mit Klarheit haben werden. Insbesondere std::as_const ist auch nett und prägnant.

Jedoch mit einer Code-Basis vor C ++ 17 müssten Sie eine verwenden const_cast. Es ist ein gutartiger, weil es fügt hinzu const und es ist auch nicht allzu ausführlich. Aber ich würde es nach dem allgemeinen Prinzip vermeiden, dass ich ein const_cast in einem Stück Code ist auf den ersten Blick immer ein bisschen beängstigend. Eine weitere gute Möglichkeit, wie @Swift in den Kommentaren darauf hingewiesen hat, ist die Implementierung einer eigenen Version von as_const.


5
2018-05-04 08:58