C++ template class error with operator '<=>' (error C2678) - c++

I'm attempting to make a treap class in C++. I have the .h and .cpp files for this class, as well as a test class. My 'add' method gives me the following error when I attempt to build it: "error C2678: binary '<=>': no operator found which takes a left-hand operand of type 'E' (or there is no acceptable conversion)"
Here's my code:
Treap.h
#pragma once
#include <compare>
#include <string>
#include <random>
#include <stack>
template<class E>
class Treap
{
public:
class Node {
public:
// data fields
E data; // key for the search
int priority; // random heap priority
Node* left;
Node* right;
// omitted constructor for brevity
// omitted rotation methods for brevity
};
private:
// data fields
Node* root;
// methods
// omitted reheap method for brevity
public:
// omitted constructors for brevity
// omitted methods for brevity
};
Treap.cpp
#include "Treap.h"
template<class E>
bool Treap<E>::add(E& key, int priority) {
Node* newNode = new Node(key, priority);
if (root == nullptr) {
root = newNode;
return true;
}
else {
// if key is found in tree, return false
std::stack<Node*> nodeStack;
Node* current = root;
Node* prev = nullptr;
auto n = current->data <=> key;
nodeStack.push(current);
while (n != 0 && current != nullptr) {
prev = current;
if (n > 0)
current = current->left;
else
current = current->right;
if (current == nullptr)
break;
nodeStack.push(current);
n = current->data <=> key;
}
if (n > 0)
prev->left = newNode;
else
prev->right = newNode;
// omitted method that reheaps the treap
}
return true;
}
TreapTest.cpp
#include "pch.h"
#include "CppUnitTest.h"
#include "../Treap.cpp"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace TreapTest {
TEST_CLASS(TreapTest) {
public:
TEST_METHOD(addTest) {
Treap<std::string> test;
Assert::IsTrue(test.add(std::string("p"), 99)); // this seems to be the line causing the issue
}
};
}
The lines that cause the errors are lines 13 and 26 (aka the lines with the spaceship operator).
I'm using VS 2019 and the C++ language standard is C++20.
P.S.: I'm a newbie to C++. I'm converting my existing Java code to C++ as an exercise to understand and learn C++.
Edit: OK, I made some of the changes suggested by #cdhowie and #interjay. But none of those suggestions helped make this specific issue go away (which I somewhat expected since the code compiles without the test file in the solution, meaning that the original code compiles just fine despite my dumb mistakes).
Also, #Barry, I hope these updates better suit that standard.

Related

How do you write a function that returns a node which value corresponds to a value stored in a variable?

I stumbled across this question in an old textbook I bought ages ago, whilst strictly speaking it is not too difficult, I could not find a post here that simply answered this one question. As such I thought "Hey perhaps someone starting out might be confused about this", and so I put together the following code:
#include <iostream>
using namespace std;
// Structures
struct charNode {
char Node;
charNode *next;
};
// Functions
charNode* getCharNode(char c) {
return ; //<----- Return Node Here
}
What this needs is to be put in a class or else you must have a global variable that points to the head of that singly linked list.
An example class could look like this:
#include <iostream>
class singly_linked_list {
struct charNode {
char Node;
charNode *next;
};
public:
// find the charNode with the value `c`:
charNode* getCharNode(char c) {
for(charNode* curr = head; curr != nullptr; curr = curr->next) {
if(curr->Node == c) return curr;
}
return nullptr;
}
// add member functions to add/remove charNode's from the list etc
// and implement one of "the rule of 3" or "the rule of 5"
private:
charNode* head = nullptr;
};
You can implement getCharNode() function like in following code. I used this function for an example of creating singly linked list of chars. Also created extra function print_list() which outputs linked list to console.
Notice that I did only allocation of nodes (new operator), and didn't do deallocation (delete), I left this task for you to do, if you care about memory leaks.
Try it online!
#include <iostream>
// Structures
struct charNode {
charNode(char value, charNode * _next = nullptr)
: Node(value), next(_next) {}
char Node;
charNode *next;
};
// Functions
charNode* getCharNode(char c, charNode * next = nullptr) {
return new charNode(c, next);
}
void print_list(charNode const * node) {
if (!node)
return;
std::cout << node->Node << " ";
print_list(node->next);
}
int main() {
charNode * list = getCharNode('a',
getCharNode('b', getCharNode('c')));
print_list(list);
}
Output:
a b c

How to correctly use smart pointers inside of a class

I am creating a binary tree in C++, and I have some problems when using smart pointers inside of the node class.
When using normal pointers, everything works fine, but with smart pointers it is just not working. I think that the problem is in this line in the insert method:
'''
binaryNode* node = this; // This is working
std::shared_ptr<binaryNode> node {this}; // This throws "double free or corruption" error
std::shared_ptr<binaryNode> node = shared_from_this (); // This throws "bad weak ptr error", I am correctly inheriting from enable_shared_from_this
'''
How can I replicate binaryNode* node = this; with smart pointers?
I even tried to use public std::enable_shared_from_this<binaryNode> usuccessfully.
Thanks for your help!
Edit:
I will try to explain myself a little bit better. This is the insert() function of a binary search tree, that looks like this (this is the .cpp file):
'''
#include "binarynode.h"
binaryNode::binaryNode(int value){
this->value = value;
this->right = nullptr;
this->left = nullptr;
}
void binaryNode::insert(int value){
binaryNode* node = this;
while(true){
if(value > node->value){
if(node->right != nullptr){
node = node->right;
}else{
node->right = new binaryNode(value);
break;
}
}else if(value < node->value){
if(node->left != nullptr){
node = node->left;
}else{
node->left = new binaryNode(value);
break;
}
}else{
return;
}
}
How can I replicate this using smart pointers?
Edit 2:
This is my .h file:
'''
#ifndef BINARYNODE_H
#define BINARYNODE_H
class binaryNode
{
public:
int value;
binaryNode(int value);
binaryNode* right;
binaryNode* left;
void insert(int value);
};
#endif // BINARYNODE_H
And this is the main file:
#include <iostream>
#include "binarynode.h"
using namespace std;
void printTree(binaryNode* node){
if(node == nullptr) return;
cout << node->value << endl;
printTree(node->left);
printTree(node->right);
}
int main(){
binaryNode* bn = new binaryNode(9);
bn->insert(4);
bn->insert(20);
bn->insert(1);
bn->insert(6);
bn->insert(15);
bn->insert(170);
printTree(bn);
return 0;
}
You do not need to use shared_ptr<>.
Actually smart pointer are here to 'solve' ownership on object, thus when an object has a single owner, unique_ptr<> should be used, and when ownership is shared, shared_ptr are used. In you situation, the ownership is clear, each node owns its left and right members, thus unique_ptr can be used.
For the tree traversal problem, don't mess with smart pointers as you are not requesting any ownership, but just looking at values, thus raw pointers are ok.
So you may end up with something like this:
#include <memory>
#include <iostream>
struct binaryNode {
binaryNode(int value) : value(value) {}
void insert(int value);
int value = 0;
std::unique_ptr<binaryNode> right;
std::unique_ptr<binaryNode> left;
};
void binaryNode::insert(int value){
binaryNode* node = this;
while(true){
if(value > node->value){
if(node->right != nullptr){
node = node->right.get();
}else{
node->right = std::make_unique<binaryNode>(value);
break;
}
}else if(value < node->value){
if(node->left != nullptr){
node = node->left.get();
}else{
node->left = std::make_unique<binaryNode>(value);
break;
}
}else{
return;
}
}
}
void printTree(const binaryNode &node){
std::cout << node.value << std::endl;
if (node.left)
printTree(*node.left);
if (node.right)
printTree(*node.right);
}
int main(){
auto bn = std::make_unique<binaryNode>(9);
bn->insert(4);
bn->insert(20);
bn->insert(1);
bn->insert(6);
bn->insert(15);
bn->insert(170);
printTree(*bn);
return 0;
}
You may notice that the print does not need to take a pointer, it can work on reference.
You cannot directly convert the same raw pointer to a shared pointer more than once, because then you will have several owners that know nothing about each other, each one thinking it has full control over the object. This is why std::shared_ptr<binaryNode> node {this} gives you a double-delete.
You also cannot use shared_from_this unless there is at least one shared pointer already pointing to your object. This is why std::shared_ptr<binaryNode> node = shared_from_this () doesn't work.
If you want shared pointers, make them all shared. For example:
// binaryNode* bn = new binaryNode(9); <-- nope!
auto bn = std::make_shared<binaryNode>(9);
// binaryNode* node = this; <-- nope!
std::shared_ptr<binaryNode> node = shared_from_this();
I do not recommend using shared pointers here though. Unique pointers are more appropriate.
C++ vector can be used to support recursive data structure. It's much simpler that use smart ptr . Basicly In your Node store vector children as a member.
#include <vector>
using std::vector;
struct Node {
Node() = default;
Node(const Node &) = delete;
Node(Node &&) = default;
vector<Node> children;
};
int main()
{
Node root;
root.children.push_back(Node());
root.children.push_back(Node());
root.children[0].children.push_back(Node());
}

Singly Linked List - Segmentation Error due to Destructor implementation

I'm trying to figure out why I'm getting a seg-error from my singly linked list implementation.
I create an object of type Deque called dq1, compiler calls the destructor for it since the program is done - destructor calls remove_front() which deals with some move()'s for the head. I believe this is where the problem lies but I can't seem to figure out where exactly it is.
Debugger Info - Dont know what to make of this?
#0 0x4013ea std::unique_ptr<Node, std::default_delete<Node> >::get(this=0x8) (/usr/include/c++/6/bits/unique_ptr.h:305)
#1 0x401586 std::unique_ptr<Node, std::default_delete<Node> >::operator bool(this=0x8) (/usr/include/c++/6/bits/unique_ptr.h:319)
#2 0x40140b std::operator!=<Node, std::default_delete<Node> >(std::unique_ptr<Node, std::default_delete<Node> > const&, decltype(nullptr))(__x=<error reading variable: Cannot access memory at address 0x8>) (/usr/include/c++/6/bits/unique_ptr.h:670)
#3 0x401132 Deque::size(this=0x7fffffffe520) (Deque.cpp:75)
#4 0x4010f2 Deque::empty(this=0x7fffffffe520) (Deque.cpp:66)
#5 0x4016dd main() (/test.cpp:12)
Deque.cpp
#include "Deque.h"
#include <iostream>
#include <memory>
#include <utility>
#include <stdexcept>
using std::cout;
using std::endl;
using std::move;
Deque::~Deque()
{
while (!empty()) remove_front();
}
void Deque::insert_front(int a)
{
std::unique_ptr<Node> new_node;
new_node->val = move(a);
new_node->next = move(head); // head is wiped.
head = move(new_node); //head is init. with new_node val*/
}
int Deque::remove_front()
{
if (empty()) {throw std::runtime_error(std::string("Empty"));};
std::unique_ptr<Node> old;
int return_value = head->val;
old = move(head);
head = move(old->next);
delete &old;
return return_value;
}
bool Deque::empty() const
{
return (size() == 0);
}
int Deque::size() const
{
int size_val = 0;
const Node* p = head.get();
while ( p != NULL)
{
size_val++;
p = p->next.get();
}
return size_val;
}
test.cpp
#include <iostream>
#include "Deque.h"
using std::cout;
using std::endl;
int main()
{
Deque dq1;
return 0;
}
deque.h
#include "Node.h"
#include <memory>
class Deque{
public:
Deque() = default;
Deque(const Deque&);
~Deque(); //must use constant space
Deque& operator=(const Deque&){return *this;};
void insert_front(int);
int remove_front();
bool empty() const;
private:
friend Node;
std::unique_ptr<Node> head ;
std::unique_ptr<Node> tail ;
};
Node.h
#include "Node.h"
std::ostream& operator<<(std::ostream& out, const Node& n) {
return out << &n << ": " << n.val << " -> " << n.next.get();
}
You have UB right here:
std::unique_ptr<Node> new_node;
new_node->val = move(a);
you create a new pointer that is default initialized (points to nullptr) and you dereference it. You should initialize it with std::make_unique if you have C++14 or later or just initialize it with new:
std::unique_ptr<Node> new_node = std::make_unique<Node>(); // C++14 or later
std::unique_ptr<Node> new_node( new Node ); // pre C++14
This line also has issue:
delete &old;
this line does not make any sense. You get address of pointer itself, which is created as local variable and try to delete it. If you tried to delete data, where old points to, that is ether wrong - whole point of std::unique_ptr is to do that automatically.
This member:
std::unique_ptr<Node> tail ;
this is wrong by design, though you do not seem to use it in your code. This assumes you are going to have multiple std::unique_ptr to point to the same object. But this pointer is for unique ownership.
You seem to have issue in Deque::size() as well, but without seeing source it is impossible to say what is wrong there.
In your destructor you do not need to do anything (though it would not harm if other methods are implemented properly) - std::unqiue_ptr will destroy all data recursively.

Circularly double linked list C++

I want to implement a circularly double linked list. this list just includes these chars in a passed in string object
Here is my code, but I always get seg fault. i use a dummy head for this list
#ifndef MY_LIST_H
#define MY_LIST_H
#include <string>
#include <iostream>
using namespace std;
/**------ -----------------
* dummy |->|pred|value|next|
* ------ -----------------
* */
struct Node
{
char value;
Node *next;
Node *pred;
Node( char value): value(value), next(0), pred(0){};
};
class MyList
{
private:
Node* head;
unsigned int count; // count number of node
public:
// default constructor
MyList(): count(0)
{
head = new Node('P');
}
//Constructs a list from a passed-in string object,
MyList(const string& str): count(0)
{
Node *cur = head;
if(count == 0)
{
head-> pred = head;
head-> next = head;
}
else
{
for( unsigned i =0; i< str.length(); ++i)
{
cur->next = new Node(str.at(i));
Node *temp = cur->next;
temp-> pred = cur;
++count;
if(count == str.length())
{
cur->next->next = head;
head-> pred = cur-> next->pred;
}
}
}
}
void print() const
{
Node *cur = head->next;
while( cur != head)
{
cout << cur-> value;
cur = cur-> next;
}
}
};
#endif
You don't seem to understand constructors very well.
Only one constructor is called when you initialize your class. You can call a constructor from another constructor if you want, but that's not by defaut: Can I call a constructor from another constructor (do constructor chaining) in C++?.
In your instance, your second constructor should probably be something like this: MyList(const string& str): MyList() { ... }
That way head wil be properly initalized, and you won't create a segfault.
Additionnally you could run your code in debug mode, in the debugger, and find out the line your code crashes. using namespace ...; in a header is also a bad practice, as you don't know where your header will be included.
It's hard to say exactly what's happening without see how you're using these classes but your MyList constructor overloaded on string is broken right off the bat. It sets count to 0 so you know it will always enter the if clause and never the else.
MyList(const string& str): count(0)
{
Node *cur = head;
if(count == 0)
{
head-> pred = head;
head-> next = head;
}
else . . .
inside the if statement, it tries to dereference head which has never been assigned a value. You do set it in the default constructor but that one also doesn't seem to do anything else.
The purpose of a constructor is to construct a valid object from scratch. Sometimes one constructor overload might delegate to another to avoid repeated code but I'm not sure what you're trying to do here.
Assuming the second constructor was meant to actually be a helper method, well it still doesn't work because count never goes above zero (except in the else clause but you can't get there with count==0).
I'll admit I didn't look very carefully but I'm guessing that if execution this far:
cur->next->next
is not always going to be set when you try to access it. if cur->next is nullptr then your program dies.

C++ Visual Studio 2010, compile error C3867 when implementing dynamic stack

Currently working on an assignment for my datastructures class in university using stacks with dynamic memory allocation. Currently I'm getting a compile error C3867 saying I'm missing a function call from an argument list. I'm not really understanding where this error is coming from / I'm having trouble identifying what exactly is my error in my code; so I was wondering if someone might be kind enough to explain to me what it is, and maybe a friendly tip to remember so I can not have this happen again.
also, I apologize for the poor formatting, I've never posted here before sorry if its hard to read. :(
code posted below.
Thanks, and regards. :P
Header File:
#ifndef STACK_H
#define STACK_H
#include <iostream>
#include <iomanip>
struct Node
{
Node *nextPtr;
int value;
};
class Stack
{
public:
//Constructors and Deconstructers Here
Stack(); //Default Constructor
~Stack(); //Default Deconstructor
//logical methods || functions here
bool isEmpty(void); //prototype checks if stack is empty
//stack operations || function prototypes here
void push(int); //prototype to push values of type int onto the stack
int pop(); //prototype to pop values off of the stack and return a value
int top(); //prototype to return the top value
private:
Node *topPtr; //pointer to class Node Object, specifically for the top of the stack
};
#endif
Class File:
#include "CPTN278_A3_Stack_Arsenault.h"
using namespace std;
Stack::Stack()
{
topPtr = 0; //set the top pointer equal to zero.
}
Stack::~Stack()
{
while (!Stack::isEmpty())
{
Stack::pop();
}
}
bool Stack::isEmpty()
{
if(top == 0)
{
return true;
}
else
{
return false;
}
}
void Stack::push(int valueTMP)
{
Node *itemPtr = new Node;
itemPtr->nextPtr = topPtr;
itemPtr->value = valueTMP;
topPtr = itemPtr;
return;
}
int Stack::pop()
{
int returnValue; //unintialized int
Node *itemPtr; //unintialized pointer to node
returnValue = topPtr->value;
itemPtr = topPtr;
topPtr = itemPtr->nextPtr;
delete itemPtr;
return returnValue;
}
int Stack::top(void)
{
return topPtr->value; //**this is where my error is being thrown**
}
bool Stack::isEmpty()
{
if(top == 0) // <-- here is the problem
{
return true;
}
else
{
return false;
}
}
top is a function, to check its result you need top(). But I think you should be testing topPtr instead.
Before you had:
bool Stack::isEmpty()
{
if(top == 0) <-- top is a function, did you mean 'topPtr'?
{
return true;
}
else
{
return false;
}
}
Fix:
bool Stack::isEmpty()
{
return topPtr == 0;
}
That's your only build error. I'm not sure why you or the compiler thought it was in the top method. That's perfectly fine. Also there's no need to write:
if (expression_that_is_true_or_false)
return true;
else
return false;
Just do:
return expression_that_is_true_or_false;
I might be borderline preaching style here, but try to get used to understanding expressions this way. Expressions involving logical operators like ==, !=, &&, ||, <, >, etc. evaluate to true or false, so there is no need to do conditional branching only to then return what the expression originally evaluated to in the first place.
Oh and I realize this is homework, but check out std::stack later in your free time if you haven't already.
In function Stack::isEmpty(), something wrong with top.
bool Stack::isEmpty()
{
if(top == 0) // here
{
return true;
}
else
{
return false;
}
}
I think it should be as below:
if(topPtr==0)
...