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?
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);
}
Related
first I want to say that using references to Value in this example is a must.
So,I have a tree (for simplicity, this is tree with nodes that can only have one child, this code is not my original assignment, but the problem is the same) and I want to store values in it.
To export the values, i have to use vector of std::pairs, with key and reference to value.
I'm trying to do that in a recursive function 'col', to push_back the values one after another.
My problem is, the final values are different. When i switched references to pointers, it worked properly. But as I said, it has to be references.
I don't understand, whats the difference between those two in this case? They both should point to a memory on a heap and that address should stay the same, right?
Here is the code:
#include <memory>
#include <vector>
#include <iostream>
template <typename Value>
class Tree {
public:
class Node {
std::unique_ptr<Value> value;
std::unique_ptr<Node> child;
public:
friend Tree;
Node(const Value i) : value(std::make_unique<Value>(i)) {}
Value* getvalue() { return value.get();}
Node* getchild() { return child.get();}
};
const std::vector<std::pair<std::string,const Value&>> collect() {
std::vector<std::pair<std::string,const Value&>> list;
col(list, root.get());
return list;
}
void col(std::vector<std::pair<std::string,const Value&>>& list, Node* node) {
list.push_back(std::make_pair("k", *node->getvalue()));
if (node->getchild() != nullptr) {
col(list, node->getchild());
}
}
void addNode(const Value i) {
add(root.get(), i);
}
Node* getroot() { return root.get();}
private:
std::unique_ptr<Node> root = std::make_unique<Node>(0);
void add(Node* node, const Value& i) {
if (node->getchild() == nullptr) {
node->child = std::make_unique<Node>(i);
} else {
add(node->getchild(), i);
}
}
};
int main() {
Tree <int>t;
t.addNode(1);
t.addNode(2);
t.addNode(3);
auto a = t.collect();
for (auto p : a) {
std::cout << p.first << " " << p.second << "\n";
}
}
The output is:
k 0
k -424282688
k -424282688
k 0
(and is different after each call)
list.push_back(std::make_pair("k", *node->getvalue()));
std::make_pair deduces the template arguments for std::pair from its function arguments, and never uses reference types for them (note the std::decay part on the linked page). So the return type of make_pair is std::pair<const char*, Value>, instantiated as std::pair<const char*, int>. The int second; member of this pair is a copy of *node->getvalue().
vector<T>::push_back(T&&) requires an argument of the actual element type, which here is T = std::pair<std::string, const int&>. There's an implicit conversion from std::pair<const char*, int> to std::pair<std::string, const int&>: the std::string first; member is constructed from the raw string pointer, and the const int& second; member is bound to the member of the input pair.
But the std::pair<const char*, int> was a temporary, so as soon as the statement ends, the lifetime of the copied and referenced value is over. Next time you try to use the reference, bang.
Instead of using make_pair, specify the exact type you need:
list.push_back(std::pair<std::string, const Value&>("k", *node->getvalue()));
or
list.push_back(decltype(list)::value_type("k", *node->getvalue()));
or put a using OutPairType = std::pair<std::string, const Value&>; in the class definition and use that instead of typing it out elsewhere.
Also note that when a std::pair has reference members, like the default behavior of any struct with reference members, the operator= copy and move assignment operators will copy or move the referenced objects. They will not (and cannot) change the reference member to refer to the same object as the right hand side's member, like a pointer assignment would. And std::vector sometimes uses operator= of its value_type (though not in push_back). You might want to consider using std::pair<std::string,std::reference_wrapper<const Value>> instead.
I am trying to write a graph data structure implementation that I feel satisfied with. (Maintain adjacency lists as sets instead of linked lists.) Anyways, I tried to use references and iterators and wrote this:
#include <iostream>
#include <string>
#include <set>
#include <stack>
class Vertex {
std::string name;
std::set<Vertex> edges;
public:
Vertex(std::string name) : name(name) {}
std::string vertexName() const {
return name;
}
std::set<Vertex> outEdges() const {
return edges;
}
void addEdge(const Vertex& other) {
edges.insert(other);
}
void removeEdge(const Vertex& other) {
edges.erase(other);
}
int outDegree() {
return edges.size();
}
};
bool operator<(const Vertex& v1, const Vertex& v2) {
return (v1.vertexName().compare(v2.vertexName()) < 0);
}
void DFS(const Vertex& v) {
std::stack<Vertex*> stack;
std::set<Vertex*> visited;
stack.push(&v); // error1
visited.insert(&v); // error2
while (!stack.empty()) {
Vertex* vert_ptr = stack.top();
stack.pop();
std::cout << vert_ptr->vertexName() << std::endl;
//
for (std::set<Vertex>::iterator iter = vert_ptr->outEdges().begin(); iter != vert_ptr->outEdges().end(); iter++) {
if (visited.find(&(*iter)) != visited.end()) { // error3
stack.push(&(*iter)); // error4
visited.insert(&(*iter)); // error5
}
}
}
}
int main() {
Vertex a = Vertex("a");
Vertex b = Vertex("b");
Vertex c = Vertex("c");
DFS(a);
getchar();
return 0;
}
I am getting the following errors:
error1: E0304 no instance of overloaded function "std::stack<_Ty, _Container>::push [with _Ty=Vertex *, _Container=std::deque<Vertex *, std::allocator<Vertex *>>]" matches the argument list
error2: E0304 no instance of overloaded function "std::set<_Kty, _Pr, _Alloc>::insert [with _Kty=Vertex *, _Pr=std::less<Vertex *>, _Alloc=std::allocator<Vertex *>]" matches the argument list
error3: E0304 no instance of overloaded function "std::set<_Kty, _Pr, _Alloc>::find [with _Kty=Vertex *, _Pr=std::less<Vertex *>, _Alloc=std::allocator<Vertex *>]" matches the argument list
error4: E0304 no instance of overloaded function "std::stack<_Ty, _Container>::push [with _Ty=Vertex *, _Container=std::deque<Vertex *, std::allocator<Vertex *>>]" matches the argument list
error5: E0304 no instance of overloaded function "std::set<_Kty, _Pr, _Alloc>::insert [with _Kty=Vertex *, _Pr=std::less<Vertex *>, _Alloc=std::allocator<Vertex *>]" matches the argument list
I am realizing that I do not understand references as well as I thought I did. I used google, and the hits I got reiterate what I understand about references, but do not touch on the part I do not understand (which is causing those errors).
I also dereferenced the iterators, and then used & to get the addresses of the actual objects the iterators are pointing to, and do not know if I am misunderstanding the way iterators work, or if it is just a problem with references.
I would appreciate it if someone could point me towards a good reference on all of this. :(
In your case void DFS(const Vertex& v) v is a reference to a var which is constant. In other words, you promised that the function will not modify the object.
std::stack<Vertex*> stack;
std::set<Vertex*> visited;
The above are containers of pointers to an object, which is not a constant and therefore is modifiable.
Here you are trying to violate an agreement. You are trying to allocate a pointer to a constant object v in a container which is intended for pointers to modifiable objects. If this is would have been allowed, you would be able to modify the value referenced by v through the pointer. So, it is not allowed and the compiler produces an error here.
stack.push(&v); // error1
visited.insert(&v); // error2
so, you needed to declare containers with pointers to the constants:
std::stack<const Vertex*> stack;
std::set<const Vertex*> visited;
now, the visited.find(&(*iter)) has to do with the implementation of the set::iterator. Apparently the value returned by operator '*' referenced a constant value, causing another conversion attempt from 'const' to non-const.
So, declaring stack and visited with const Vertex * argument should solve your compilation issues.
I wrote a function to compute the union of two sets.
I'm running into several compilation errors and I believe that's in part due to how I made the StringUnion array and declared it but nothing I do is working thus far.
This is my header file.
#ifndef StringSet_header
#define StringSet_header
#include <memory>
#include <string>
using std::string;
using std::unique_ptr;
using std::make_unique;
class StringSet{
public:
//create an empty set
StringSet() = default;
StringSet(int capacity);
//copy a set
StringSet(const StringSet &);
StringSet& operator[](const int);
//Insert a string to the set
bool insert(string);
//Remove a string from the set
bool remove(string);
//Test whether a string is in the set
int find(string) const;
//Get the size of the set
int size() const;
//get string at position i
string get(int i) const;
//Return the set union of the set and another StringSet
StringSet setunion(const StringSet&) const;
//Return the intersection of the set and another StringSet
StringSet intersection(const StringSet&) const;
//Return the set diffference of the set and another StringSet
StringSet difference(const StringSet&) const;
//prevent default copy assignment
StringSet& operator=(const StringSet&) = delete;
int NOT_FOUND = -1;
static constexpr int def_capacity {4};
private:
int arrSize {def_capacity};
int currentSize {0};
unique_ptr<string[]> arr {make_unique<string[]>(def_capacity)};
};
#endif
And this is my implementation of my SetUnion function.
StringSet StringSet::setunion(const StringSet &Array2) const
{
StringSet StringUnion = make_unique<string[]>(arrSize);
if (currentSize > 0)
{
for (auto i=0; i < currentSize; i++)
{
auto s = arr[i];
StringUnion.insert(s);
}
for (auto i=0; i < Array2.currentSize; i++)
{
auto s = Array2[i];
if (StringUnion.find(s) == NOT_FOUND)
{
StringUnion.insert(s);
}
}
}
else
{
auto result = StringSet();
return result; //return empty StringSet}
}
}
Errors:
|error: conversion from 'std::_MakeUniq<std::basic_string<char> []>::__array {aka std::unique_ptr<std::basic_string<char> []>}' to non-scalar type 'StringSet' requested|
error: passing 'const StringSet' as 'this' argument discards qualifiers [-fpermissive]
error: no matching function for call to 'StringSet::find(StringSet&)'
error: no matching function for call to 'StringSet::insert(StringSet&)'
Insert and find work as intended to and I was able to use insert and find functions within my remove function and some others so why can't I use them here?
In your line
StringSet StringUnion = make_unique<string[]>(arrSize);
The RHS uses the c++14 construct that takes an std::size_t, and returns an std::unique_ptr<std::string> internally pointing to an array.
The LHS, however, is a StringSet object.
You did not define a constructor taking such a type, so it's a problem.
Looking at your code, StringSet does have a std::unique_ptr<std::string> member, so you could add a ctor taking such an object, and initializing the member from it. However, it's unclear what would be the benefit of such a ctor, as you already have a ctor
StringSet(int capacity);
which already essentially does the same.
As Leon writes, you should just use this one instead of the line you have
StringSet StringUnion(arrSize);
The errors provides by your compiler seem pretty clear. Let's check them.
conversion from std::make_unique ... to non-scalar type StringSet requested
It's because of the definition of the function std::make_unique, which returns a std::unique_ptr<T>. But you're trying to assign it to a value of type StringSet. There is no constructor or operator for creating a StringSet from a std::unique_ptr, so the compiler complains that he can't do that.
error: no matching function for call to 'StringSet::find(StringSet&)'
Your class StringSet has an operator[] that returns a reference on a StringSet so auto s = Array2[i]; is of type StringSet. But your functions find and insert ask for a std::string. As there is no constructor that can provide implicit conversion from StringSet to std::string, the compiler complains.
I have the following code:
#include <map>
using namespace std;
struct A {};
map</*const*/ A *, int> data;
int get_attached_value(const A *p) {
return data.at(p);
}
void reset_all() {
for (const auto &p : data) *p.first = A();
}
My problem is that this code fails on a type error both when I comment and uncomment the const in the type of data. Is there any way I can solve this without using const_cast and without losing the const in get_attached_value?
The problem seems to be in the pointee type, which has to be the same in both pointer declarations (map key type and the get_attached_value's argument).
OP's code uses const A*, which is a pointer to a const instance of class A (an alternative spelling is A const *). Leaving this const in both map declaration and in get_attached_value' argument almost works, but reset_all does not allow you to assign a new value to *p.first, because the resulting type is A const& (which cannot be assigned into).
Removing both consts works as well, but OP wants to keep a const in get_attached_value.
One solution for OP's requirements, keeping as many consts as possible, seems to be to change the pointer type to a const pointer to a non-const instance of A. This will keep reset_all working, while allowing to use a const pointer in both map declaration and get_attached_value's argument:
#include <map>
using namespace std;
struct A {};
map<A * const, int> data;
int get_attached_value(A * const p) {
return data.at(p);
}
void reset_all() {
for (const auto &p : data)
*p.first = A();
}
Another possible solution, with map's key as non-const but the get_attached_value's parameter const, could use std::lower_bound with a custom comparator to replace the data.at() call:
#include <map>
#include <algorithm>
using namespace std;
struct A {};
map<A*, int> data;
int get_attached_value(A const * const p) {
auto it = std::lower_bound(data.begin(), data.end(), p,
[] (const std::pair<A* const, int>& a, A const* const b) {
return a.first < b;
}
);
return it->second;
}
void reset_all() {
for (const auto &p : data)
*p.first = A();
}
However, this solution will be significantly less efficient than one that would use map's native search functions - std::lower_bound uses linear search when input iterators are not random access.
To conclude, the most efficient solution in C++11 or lower would probably use a const pointer as the map's key, and a const_cast in the reset_all function.
A bit more reading about const notation and pointers can be found here.
This question already has answers here:
Is casting std::pair<T1, T2> const& to std::pair<T1 const, T2> const& safe?
(3 answers)
Closed 8 years ago.
This code demonstrates the problem I'm trying to solve:
#include <map>
class Point
{
public:
float m_x;
float m_y;
};
typedef std::set<Point *> PointSet;
typedef std::set<const Point * const> ConstPointSet;
float GetMinimumRange(const ConstPointSet &pointSet)
{
float minimumRange(0.0f);
// find the smallest distance between any pair of points in the set
return minimumRange;
}
float GetMinimumRangeWrong(const PointSet &pointSet)
{
PointSet::iterator first(pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
class PointSet_
{
public:
std::set<Point *> m_pointSet;
float GetMinumumRange() const
{
PointSet::iterator first(m_pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
};
void test()
{
PointSet myPointSet;
// Add some points to my set
// This fails because the compiler states it can't convert from PointSet to ConstPointSet.
//float minimumRange1(GetMinimumRange(myPointSet));
// reinterpret_cast<> is the only cast that works here, const_cast fails with the same
// complaint as the line above generates
ConstPointSet *myConstPointSet(reinterpret_cast<ConstPointSet *>(&myPointSet));
float minimumRange1(GetMinimumRange(*myConstPointSet));
float minimumRange2(GetMinimumRangeWrong(myPointSet));
}
I want to create a routine that takes a PointSet, evaluates the minimum range between any pair of Points in the set, but that it guarantees that it won't modify the PointSet passed to it in any way at all. It can't modify the members of any referenced Point, it can't change the pointers themselves, nor can it add or remove members from the set
The issue is that the compiler correctly views PointSet and ConstPointSet as different types because of the difference of const qualifiers of the inner type, and therefore refuses to cast between them, even though I'm only adding const qualifiers.
I tried creating a class to contain a PointSet, and creating a const member function, but even in there it allows modification to one of the inner Points. At least MSVC will compile that without complaint. I'll confess I was quite surprised about this.
The only way I've found that works is to use a reinterpret_cast<> to convert a pointer to a PointSet to a pointer to a ConstPointSet. The standard does note that reinterpret_cast<> can be used to add const qualifiers, but does that apply in this case?
If not, is there any way to do what I want? I realize that good code discipline can be used to ensure that GetMinimumRange() doesn't modify the passed PointSet, but I'd like to get those const qualifiers in there for two reasons.
They will ensure that if anyone ever modifies GetMinimumRange() they can't cause it to modify the PointSet.
It will allow the compiler to optimize over the call to GetMinimumRange(). In the absence of the const qualifiers, no assumptions can be made at the calling site regarding values that could be cached across the call, thus possibly leading to redundant fetches of data.
There is no straightforward way, because constness does not propagate through pointers. In a const PointSet, it's the pointers themselves that are const, not the objects they point to. And, like you've discovered, const Point * is a different type from Point *, so std::set<const Point *> is a different type from std::set<Point *>.
I don't like the reinterpret_cast of a STL structure. That is scary to me. STL does all kinds of optimizations based on the type of template parameters. std::vector<bool> being an extreme example. You'd think that std::set<T *> and std::set<const T *> would be laid out the same because they are both pointers, but I wouldn't assume so until I read it in the Standard.
If it were a structure I had written myself, and I could easily verify that the cast would work, it would be less scary but still ugly.
You could write a wrapper class that holds a reference to a std::set<Point *> but only allows const access to its pointed-to Points via iterators. If the pointers are guaranteed to be non-null, your iterator can dereference the points directly. I've written it here as a template:
template <typename T>
class PointerSetViewer
{
public:
PointerSetViewer(std::set<T *> const &set) : set(set) {}
struct iterator : public std::iterator<std::forward_iterator_tag, T const>
{
iterator(typename std::set<T *>::const_iterator it) : it(it) {}
T const &operator*() const { return **it; }
T const *operator->() const { return *it; }
iterator &operator++() { ++it; return *this; }
bool operator==(iterator other) { return it == other.it; }
bool operator!=(iterator other) { return it != other.it; }
private:
typename std::set<T *>::const_iterator it;
};
iterator begin() { return iterator(set.cbegin()); }
iterator end() { return iterator(set.cend()); }
private:
std::set<T *> const &set;
};
It's bulky, but it accomplishes your goals without doing anything risky:
float GetMinimumRangeWrong(PointerSetViewer<Point> &pointSet)
{
PointerSetViewer<Point>::iterator first(pointSet.begin());
first->m_x = 42.0f; // does not compile
}
Also if you're using C++11, you can get some nice range-based for loops:
template <typename T>
PointerSetViewer<T> view_set(std::set<T *> const &set) {
return PointerSetViewer<T>(set);
}
for (Point const &p : view_set(myPointSet)) {
// whatever...
}
Baroque? Yes, but if one piece of baroque library code lets you write 100 pieces of beautiful application code with better type checking, it's probably worth it.
Edit: this doesn't work for set. As pointed out in comments, a non-const set is defined to hold const T, so there is actually nothing we can do.
At this stage I don't see a viable solution other than making PointSet_ actually wrap the set properly, i.e. have the set be private and be careful in your public functions.
Here is a solution I came up with; make the set contain a little wrapper which will propagate the const-ness of itself onto the pointer.
I would have thought there would be a pre-existing class that does this, but none of the std smart pointer classes seem to.
#include <iostream>
#include <set>
template<typename T>
struct qualifier_ptr
{
T *operator->() { return ptr; }
T const *operator->() const { return ptr; }
operator T*() { return ptr; }
operator T const*() const { return ptr; }
qualifier_ptr(T *p): ptr(p) {}
private:
T *ptr;
};
struct Point
{
float m_x;
float m_y;
};
struct PointSet
{
typedef std::set< qualifier_ptr<Point> > SetType;
SetType points;
float foo() const
{
//Point *p = *points.begin(); // error
Point const *p = *points.begin(); // OK
return 0;
}
};
int main()
{
PointSet ps;
PointSet const &cps = ps;
ps.foo(); // OK
cps.foo(); // OK
}
I normally don't like to use conversion operators but it seems appropriate here.
As you stated in the comments that the set is built only once per session, I'd suggest just creating the ConstPointerSet by making a copy:
void test()
{
PointSet myPointSet;
// Add some points to my set
ConstPointSet myConstPointSet{ begin(myPointSet), end(myPointSet) };
float minimumRange1(GetMinimumRange(myConstPointSet));
}
Or wrapp it into a function:
ConstPointSet toConst(const PointSet& pSet){
return ConstPointSet{ cbegin(pSet), cend(pSet) };
}
If you don't need the semantics of a set I'd recommend using a std::vector instead, which is much more efficient to copy or traverse.