Почему метод getNodeSuccess работает, а не getNodeFail? - PullRequest
0 голосов
/ 24 марта 2020

Меня смущает поведение следующего кода: я не понимаю, почему метод getNodeSuccess работает, но метод getNodeFail не удается построить. Единственное различие между этими двумя методами состоит в том, что один использует оператор [] для доступа к карте, а другой - метод at. Я думал, что обе эти операции возвращают ссылку на элемент на карте, и обе должны в основном делать то же самое. Однако, когда я использую оператор [] вместо метода at(), он пытается вызвать конструктор по умолчанию класса GraphNode. Поскольку конструктор по умолчанию является закрытым, сборка завершается неудачей.

Для демонстрации я сделал конструктор по умолчанию класса GraphNode закрытым. Если я изменю его на publi c, то это сработает.

Может кто-нибудь объяснить, что здесь происходит? Почему один метод вызывает конструктор по умолчанию, а не другой?

#include <vector>
#include <list>
#include <unordered_map>
#include <exception>

template <class T>
struct GraphNode
{
    int id;
    T data;
    std::list<int> adj; // adjacency list

    GraphNode(const int id) 
    {
        this->id = id;
    }
private:
    GraphNode() {}
};

template<class T>
struct UndirectedGraph 
{
    std::unordered_map<int, GraphNode<T>> lookup;
    std::vector<GraphNode<T>> vertices;

    void addNode(const GraphNode<T> node) 
    {
        // if the id of the node already exists, than we cannot add it
        if (lookup.find(node.id) == lookup.end())
            lookup.insert(std::make_pair(node.id, node));
        else
            throw std::runtime_error("Trying to add a node that already exists.");
    }

    void addEdge(const int source, const int destination) 
    {
        getNodeSuccess(source).adj.push_back(destination);
        getNodeSuccess(destination).adj.push_back(source);

        getNodeFail(source).adj.push_back(destination);
        getNodeFail(destination).adj.push_back(source);
    }

    GraphNode<T>& getNodeSuccess(const int id)
    {
        if (lookup.find(id) == lookup.end())
            throw std::runtime_error("Trying to retrieve a node that doesn't exist");

        return lookup.at(id);
    }

    GraphNode<T>& getNodeFail(const int id)
    {
        if (lookup.find(id) == lookup.end())
            throw std::runtime_error("Trying to retrieve a node that doesn't exist");

        return lookup[id];
    }
};

int main(int argc, char* argv[]) 
{
    UndirectedGraph<int> graph;

    graph.addNode(GraphNode<int>(1));
    graph.addNode(GraphNode<int>(3));
    graph.addEdge(1, 3);

    return 0;
}

1 Ответ

2 голосов
/ 24 марта 2020

operator[] будет либо return ссылкой на значение с этим ключом, либо создаст его с помощью конструктора по умолчанию, вставит его, а return будет ссылкой на вновь созданное значение. Таким образом, ваша проблема в том, что operator[] должен иметь возможность, по крайней мере, вызывать конструктор по умолчанию, , даже если вы знаете, что ключ всегда будет присутствовать .

Если вы просто хотите посмотреть, что-то в вашем unordered_map, ваше использование find уже близко.

GraphNode<T>& getNodeFail(const int id)
{
    auto it = lookup.find(id);
    if (it == lookup.end())
        throw std::runtime_error("Trying to retrieve a node that doesn't exist");
    return it->second;
}

Это также сэкономит вам дополнительный поиск, поскольку вы уже выполнили работу один раз с find.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...