C++ - String Array Parameter for SetUnion function - c++

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.

Related

C++ error: passing ‘const umap_int {aka const std::unordered_map<int, int>}’ as ‘this’ argument discards qualifiers [-fpermissive]

I am trying to get a constant reference back by a method for the mapped value of an unordered_map. The unordered_map is a class member. However, the code below does not work and raises the error stated in the title.
I tried to change const umap_int::mapped_type & to const int & which did not work either. The standard example of returning a const reference to a variable of simple datatype (int, double, ...) worked.
#include <unordered_map>
using namespace std;
typedef unordered_map<int, int> umap_int;
class class_A{
public:
class_A(){
for(int k=0; k<3;++k)
M[k] = k;
}
const umap_int::mapped_type & get_M(int key) const{
return M[key];
}
private:
umap_int M;
};
int main(){
class_A A;
return 0;
}
Inside const method you can only call for M its const member functions. Both unordered_map::operator[] overloads are non-const - reference. So you cannot use it inside const get_M. You could remove const qualifier from get_M signature, or use find which has const overload but then you need to handle a case when mapped value doesn't exist for the passed key:
const umap_int::mapped_type & get_M(int key) const {
//return M[key];
auto it = M.find(key);
if (it != M.end())
return it->second;
// do sth here ...
// throw exception
// make static variable with default value which will be accessed
}

Inserting objects into vector using lower_bound with compare struct

I have this class:
class Mail {
public:
Mail(const string & msg) : msg(msg) {}
const string msg;
};
And this structure, which compares two Mail objects:
struct Compare {
bool operator()(const Mail & mail, Mail const & mail2) const {
return mail.msg < mail2.msg;
}
};
I want to have a vector with Mail objects sorted by their message const string msg. However, when I try to insert new object into vector using lower_bound, I get many errors, including:
passing ‘const string as ‘this’ argument discards qualifiers.
int main() {
vector <Mail> mails;
Mail mail2("1");
mails.push_back(mail2);
const string msg = "2";
Mail mail(msg);
auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());
// mails.push_back(mail); // OK
mails.insert(low, mail); // passing ‘const string as ‘this’ argument discards qualifiers
return 0;
}
I dont yet understand const usage much and can't figure out, which const is wrong.
I am sorry, if this has already been asked, but I haven't found an answer to this problem yet.
The errors in C++ can sometimes be hard to diagnose. My tip is to always start at the top and resolve that one first. In this case, there's a long list of them, but they're all really about the same thing -- the assignment operator for Mail cannot be generated.
Think of it this way, the compiler is being helpful and is trying to generate (and inside lower_bound(), use) this function:
Mail& operator=( const& Mail mail )
{
msg = mail.msg;
return *this;
}
But it can't because that assignment in the body is invalid due to msg being const. You can't really write it yourself either since you also can't assign to a const variable.
Usually you don't need member variables to be const because they become const if the instance of the class is itself const:
const auto mail1 = Mail{"1"};
auto mail2 = Mail{"2"};
mail1.msg = "3"; // FAIL! msg is const since mail1 is const
mail2.msg = "4"; // Ok! msg is not const
If you do need a const member, you can't use assignment operators with the class. Them's the breaks.
Remove that const and all works:
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class Mail {
public:
Mail(const string & msg) : msg(msg) {}
string msg; //////////////////////////////// Not const!
};
struct Compare {
bool operator()(const Mail & mail, Mail const & mail2) const {
return mail.msg < mail2.msg;
}
};
int main() {
vector <Mail> mails;
Mail mail2("1");
mails.push_back(mail2);
const string msg = "2";
Mail mail(msg);
auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());
// mails.push_back(mail); // OK
mails.insert(low, mail); // OK!
return 0;
}
See it run live on Coliru.
Footnotes:
You could use a lambda for the comaparator to avoid some boilerplate around the class Compare:
const auto low = lower_bound( begin(mails), end(mails), mail,
[]( const auto& mail1, const auto& mail2 )
{ return mail1.msg < mail2.msg; } );
You can use vector::emplace_back() to construct items in place, avoiding a copy. The following blocks do the same thing in effect, but the second is more efficient:
const auto mail = Mail{"2"};
mails.push_back( mail2 ); // Copies
mails.emplace_back("2"); // Creates it right in the vector
Consider using vector::reserve() if you know how many items you'll put in your vector.
The problems here pertain to the deleted copy assignment operator and the deleted move assignment operator because of the const string msg; member in the Mail class:
Deleted implicitly-declared copy assignment operator
A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member of non-class type (or array thereof) that is const;
Deleted implicitly-declared move assignment operator
The implicitly-declared or defaulted move assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member that is const;

I am confused about constant, constant references, references to constants, ... (including example code)

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.

C++ - Creating a Copy function without = assignment for a Dynamic String Array

Trying to write a copy function for a dynamically allocated array.
In my header file I have:
#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)};
};
In my implementation file I have:
#include "StringSet.h"
#include <iostream>
#include <utility>
StringSet::StringSet(int capacity)
: arrSize{capacity},
arr{make_unique<string[]>(capacity)}
{
}
StringSet::StringSet(const StringSet& a)
{
auto a2 = StringSet(currentSize);
for (auto i=0; i < currentSize ; i++ )
{
a2[i] = a[i];
}
}
Compiler error:
error: constructors may not be cv-qualified
error: no match for 'operator=' (operand types are 'StringSet' and 'std::string {aka std::basic_string<char>}')
error: passing 'const StringSet' as 'this' argument discards qualifiers [-fpermissive]
error: use of deleted function 'StringSet& StringSet::operator=(const StringSet&)'
My assignment has overloaded the assignment operator= and thus I'm not able to use that here. Is there another way of implementing a copy function without using the assignment operator - is there anything in std::string that allows us to copy contents easier in this manner?
If there's anything else I need to add here for details please let me know.
Thank you.
The problem with this code:
StringSet::StringSet(const StringSet& a)
{
auto a2 = StringSet(currentSize);
for (auto i=0; i < currentSize ; i++ )
{
a2[i] = a[i];
}
}
is that, even if it compiled, you're never actually initializing the members of this... you're initializing some temporary a2 that goes out of scope at the end of the constructor. You actually want:
StringSet::StringSet(const StringSet& a)
: StringSet(a.arrSize)
{
currentSize = a.currentSize;
for (auto i=0; i < currentSize; i++ )
{
arr[i] = a.arr[i];
}
}
Also, your operator[] returns a StringSet& where it should probably return a std::string&.
Also, you should avoid bringing names into the global namespace like you're doing. Keep it local. Writing std:: is not a burden.

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

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