Why am I getting an error, "non-const lvalue reference to type 'std::basic_string<char>' cannot bind to a value of unrelated type 'const char [4]'"

48 views Asked by At

I had a queue and originally worked with strings and now I added templates to it so that if I decide to add int doubles, etc, it would still work. When using strings to add to my queue, I get an error where it says "non-const lvalue reference to type 'std::basic_string<char>' cannot bind to a value of unrelated type 'const char [4]'". Does this mean it is reading the type string as type char?

#include <iostream>
using namespace std;

template <class Type>
struct Node {
  Type data;
  Node<Type>* nextNode;
  Node(Type& data) : data(data), nextNode(nullptr) {}
};

template <class Type>
class Queue {
  public:
  Node<Type>* head;
  Node<Type>* tail;

  Queue() : head(nullptr), tail(nullptr) {}

  ~Queue(){
    while (!isEmpty()){
      remove();
    }
  }

  bool isEmpty() {
   return head == nullptr;
  }

  void add(Type &data){
    Node<Type>* newNode = new Node<Type>(data);
    if (isEmpty()){
      head = newNode;
      tail = newNode;
    } else {
      Node<Type>* temp = tail;
      temp->nextNode = newNode;
      tail = newNode;
    }
  }

  void remove(){
    if (isEmpty()){
      std::cout << "Not able to remove from empty queue.";
      return;
    }
    Node<Type>* temp = head;
    head = head->nextNode;
    delete temp;
  }

  Type peek(){
    if (isEmpty()){
      cout << "Queue is empty. ";
      return Type();
    }
    return head->data;
  }

};

template <class Type>
ostream &operator << (ostream &out, const Queue<Type> &s){
  Node<Type>* temp = s.head;
  while (temp != nullptr) {
    out << s.head->data << " ";
    temp = temp->nextNode;
  }
  return out;
}

void fillQueue(Queue<string> &queue) {
  queue.add("one");
  queue.add("two");
  queue.add("three");
  queue.add("four");
  cout << queue << endl;
}

void check(string name, const string shouldBe, string currentlyIs) {
  if (shouldBe == currentlyIs) {
    cout << name << ": Passed " << endl;
  } else {
    cout << name << ": Failed, value should be " << shouldBe
         << " but is returning " << currentlyIs << endl;
  }
}

void check(string name, bool shouldBe, bool currentlyIs) {
  if (shouldBe == currentlyIs) {
    cout << name << ": Passed " << endl;
  } else {
    cout << name << ": Failed, value should be " << shouldBe
         << " but is returning " << currentlyIs << endl;
  }
}

void testIsEmpty() {
  Queue<string> queue;
  check("1. Checking Empty Queue", queue.isEmpty(), true);
  queue.add("one");
  queue.remove();
  check("2. Checking Recently Empty Queue", queue.isEmpty(), true);
}

void checkQueueOrder() {
  Queue<string> queue;
  fillQueue(queue);
  string txt = "one";
  check("3. Check Front: ", queue.peek(), txt);
  queue.remove();
  txt = "two";
  check("4. Check Second: ", queue.peek(), txt);
}


int main() {
  testIsEmpty();
  checkQueueOrder();
}

These are the lines where I get these errors:

void fillQueue(Queue<string> &queue) {
  queue.add("one");
  queue.add("two");
  queue.add("three");
  queue.add("four");
  cout << queue << endl;
}

and this function

  void add(Type &data){
    Node<Type>* newNode = new Node<Type>(data);
    if (isEmpty()){
      head = newNode;
      tail = newNode;
    } else {
      Node<Type>* temp = tail;
      temp->nextNode = newNode;
      tail = newNode;
    }
  }

I get a note: "note: passing argument to parameter 'data' here void add(Type &data){"

I tried using variables:
void fillQueue(Queue<string> &queue) {
string x = "one"
queue.add(x);
queue.add("two");//etc etc.
queue.add("three");
queue.add("four");
cout << queue << endl;
}   

I also tried using type int as well and similar error pops up. (I changed code to take ints instead of strings)

void fillQueue(Queue<int> &queue) {
  queue.add(1);
  queue.add(2);
  queue.add(3);
  queue.add("4);
  cout << queue << endl;
}
2

There are 2 answers

0
paddy On

The const char [4] type is from your string literal "two" -- that is four bytes (including the string's null-terminator) of non-modifiable memory.

The error here is that your member function add(Type&) is receiving a lvalue reference, which essentially means something that is modifiable (e.g. some non-const variable belonging to the caller). But you're not allowed to change a string literal at runtime. The compiler cannot convert it that into a temporary std::string and then bind that to the reference because the language doesn't allow it.

To easily fix the problem, you should make the reference const. That will allow the compiler to automatically convert it to a temporary std::string and bind that to the reference:

void add(const Type &data)
{
    // ...
}

As a general rule, if a function does not need to modify the data being referenced, then you should make it const. That includes your Node class template. For example:

template <class Type>
struct Node {
  Type data;
  Node<Type>* nextNode;
  Node(const Type& data) : data(data), nextNode(nullptr) {}
};
0
user12002570 On

The problem is that when you wrote queue.add("one"), you're passing a string literal of type const char [4] as an argument to Queue<string>::add(std::string&) but the parameter of that member function is of type std::string& which cannot be bound to the argument "add" because a string literal is non-modifiable(note the const in const char[4]).


Solution

The solution is to add a low-level const to the parameter as shown below:

//-------vvvvv---------------->added const here
void add(const Type &data);

Additionally, for the program to compile, you'll also need to add const in the constructor parameter of Node as shown below:

//---vvvvv------------------>added const here
Node(const Type& data)

Working demo