candidate function not viable: 1st argument ('const Node *') would lose const qualifier

Kevin King picture Kevin King · Jul 28, 2015 · Viewed 12k times · Source

I am writing a DiGraph (directed graph) class with the c++ built in unordered_map<Node*, unordered_set<Edge>> data structure, where Node and Edge are two structs I defined myself. And in the class I wrote a containsNode() method to search if a Node is in the graph. This is the containsNode() method body:

bool DiGraph::containsNode(const Node * n) const {
    auto::const_iterator it = digraph.find(n);
    return (it == digraph.end());
}

digraph is the private member of DiGraph of type unordered_map<Node*, unordered_set<Edge>>.

However, the compiler generates the following error:

error: no matching member function for call to 'find'
auto::const_iterator it = digraph.find(n);
candidate function not viable: 1st argument ('const Node *') would lose const qualifier
const_iterator find(const key_type& __k) const {return __t...

However, if I declare the method as bool DiGraph::containsNode(Node* n) const {...} (the only difference being that the const keyword removed from the argument list) then there is no compilation error.

I checked the C++ documentation and saw that the find() method declaration in the unordered_map container has the const keyword:

std::unordered_map::find
    const_iterator find(const Key& key) const;

Therefore I think there shouldn't be a compilation error, so why do I get one?

Answer

user4581301 picture user4581301 · Jul 28, 2015

find() looks like this: find(const T& key) If T is Node*, then Node* must be const. But note, the pointer must be const, NOT the value pointed at which containsNode(const Node * n) will give you. find() will give no assurances that the value pointed at by n will go untouched, and that violates const Node * n.

You are in a right pickle, my friend. Since your key is the pointer, you probably can't use a copy of the pointed-at value, different address, nor can you assign it to a non-const pointer that can be used by find. You can cast, but so much for respecting the const! Rethink how you are doing this, is my advice.

Bit easier to visualize with a set. Less overhead, same results.

#include <set>
using namespace std;

class A
{

};

set<A*> test;

void func1(A *const  a) // pointer is const
{
    test.find(a); //Compiles, but not what you want.
    A b;
    a = &b; // Doesn't compile. Can't change where a points 
    *a = b; // compiles. Value at a is open game
}

void func2(const A *  a) // value is const
{
    test.find(a); //doesn't compile
    A b;
    a = &b; // compiles. Can change where a points
    *a = b; // does not compile. Can't change what a points at
    test.find((A*)a); //Compiles, but holy super yuck! Find a better way!
}

int main()
{
    A a;
    func1(&a);
    func2(&a);
}