2

As you can see that operator+ is returning an object of class type MyString.

The assignment operator is not accepting this object in Visual Studio 2019 and giving an error of E0349 that no operands matches. But, in Visual Studio 2012, it is running fine.

Code:

#include <iostream>
using namespace std;

class MyString {
private:
    char* str;
    int length;
public:
    MyString();
    MyString(const MyString&);
    bool operator!();
    friend ostream& operator<<(ostream&, MyString&);
    friend istream& operator>>(istream&, MyString&);
    MyString& operator=(const MyString&);
    MyString operator+(MyString&);
    MyString& operator-(MyString);
    MyString& operator ++ ();
    MyString& operator -- ();
    bool operator<(MyString&);
    char& operator[](int);
    char* operator()(int, int);
    void my_strcpy(char*&, char*);
    int my_strlen(char*);
};
MyString::MyString() {
    str = new char[1];
    str[0] = '\0';
    length = 0;
}
MyString::MyString(const MyString& obj) {
    int size = my_strlen(obj.str) + 1;
    str = new char[size];
    cout << "copy constructor called\n";
    my_strcpy(str, obj.str);
}
bool MyString::operator!() {
    if (str[0] == '\0')
        return true;
    return false;
}
ostream& operator<<(ostream& output, MyString& obj) {
    output << obj.str;
    return output;
}
istream& operator>>(istream& input, MyString& obj) {
    delete obj.str;
    obj.str = new char[50];
    input.getline(obj.str, 50);
    return input;
}
MyString& MyString::operator=(const MyString& obj) {
    if (this != &obj) {
        my_strcpy(str, obj.str);
    }
    return *this;
}
char& MyString::operator[](int index) {
    if (index >= 0 && index < my_strlen(str))
        return str[index];
    else {
        clog << "Index is out of range\n", exit(1);
    }
}
void MyString::my_strcpy(char*& destination, char* source) {
    int len_des = my_strlen(destination);
    int len_source = my_strlen(source);
    delete[]destination;
    destination = new char[len_source + 1];
    int index = 0;
    while (source[index] != '\0')
        destination[index] = source[index++];
    destination[index] = '\0';
}
MyString MyString::operator+(MyString& obj) {
    MyString newObj;
    int size = my_strlen(str) + my_strlen(obj.str) + 1;
    newObj.str = new char[size];
    char* temp = newObj.str;
    int index_str = 0;
    while (str[index_str] != '\0')
        *newObj.str++ = str[index_str++];
    index_str = 0;
    while (obj.str[index_str] != '\0')
        *newObj.str++ = obj.str[index_str++];
    *newObj.str = '\0';
    newObj.str = temp;
    cout << newObj.str << endl;
    return newObj;
}
MyString& MyString::operator ++ () {
    int size = my_strlen(str) + 2;
    char* new_str = new char[size];
    char* temp = new_str;
    int index = 0;
    while (str[index] != '\0')
        *new_str++ = str[index++];
    *new_str++ = 'a';
    *new_str = '\0';
    delete[]str;
    str = temp;
    return *this;
}
MyString& MyString::operator -- () {
    int size = my_strlen(str);
    char* new_str = new char[size];
    char* temp = new_str;
    int index = 0;
    while (index < size - 1)
        *new_str++ = str[index++];
    *new_str = '\0';
    delete[]str;
    str = temp;
    return *this;
}
MyString& MyString::operator-(MyString obj) {
    bool find = true;
    int obj_str_len = my_strlen(obj.str);
    int str_len = my_strlen(str);
    int start_ind = 0, end_ind = 0;
    if (str_len > obj_str_len) {
        for (int i = 0; str[i] != '\0'; i++) {
            if (str[i] == obj.str[0]) {
                start_ind = i;
                find = true;
                for (int j = i + 1, k = 1; obj.str[k] != '\0' && find; j++, k++) {
                    if (str[j] != obj.str[k]) {
                        find = false;
                    }
                    end_ind = j + 1;
                }
                if (find)
                    break;
            }
        }
        if (find) {
            while (str[end_ind] != '\0') {
                str[start_ind++] = str[end_ind++];
            }
            str[str_len - (end_ind - start_ind)] = '\0';
        }
    }
    return *this;
}
bool MyString::operator<(MyString& obj) {
    if (my_strlen(str) < my_strlen(obj.str))
        return true;
    else if (my_strlen(str) > my_strlen(obj.str))
        return false;
    for (int i = 0; str[i] != '\0'; i++) {
        if (str[i] < obj.str[i])
            return true;
        else if (str[i] > obj.str[i]) {
            return false;
        }
    }
    return false;
}
char* MyString::operator()(int starting_index, int ending_index) {
    int new_str_size = ending_index - starting_index + 2;
    char* new_str = new char[new_str_size];
    char* temp = new_str;
    int index = 0;
    int str_len = my_strlen(str);
    while (starting_index < str_len && index < 10)
        *new_str++ = str[starting_index++], index++;
    *new_str = '\0';
    return temp;
}
int MyString::my_strlen(char* str) {
    int size = 0;
    while (str[size] != '\0')
        size++;
    return size;
}

int main() {
    MyString str1, str2, str3, str4, str6;  

    if (!str1)
    {
        cout << "String 1 is Empty.\n";
        cout << "Str 1 = " << str1 << endl << endl << endl;
    }

    cout << "Enter String 1:\t";
    cin >> str1;


    cout << "Enter String 2:\t";
    cin >> str2;

    cout << "\n\n\nUser Entered:\n";
    cout << "String 1 = " << str1 << endl;
    cout << "String 2 = " << str2 << endl << endl << endl;

    cout << "Before str1 = str1; str1 = " << str1 << endl;
    str1 = str1;
    cout << "After str1 = str1, str1 = " << str1 << endl << endl << endl;

    cout << "Before str4 = str3 = str1+str2\n";
    cout << "str1 = " << str1 << endl;
    cout << "str2 = " << str2 << endl;
    cout << "str3 = " << str3 << endl;
    cout << "str4 = " << str4 << endl;

    str4 = str3 = str1 + str2;


    cout << "\n\n\nAfter str4 = str3 = str1+str2\n";
    cout << "str1 = " << str1 << endl;
    cout << "str2 = " << str2 << endl;
    cout << "str3 = " << str3 << endl;
    cout << "str4 = " << str4 << endl;

    cout << "\n\n\nEnter String 3:\t";
    cin >> str3;

    cout << "\n\n\nEnter String 4:\t";
    cin >> str4;

    cout << "\n\n\nstr3 = " << str3 << endl;
    cout << "str4 = " << str4 << endl;

    if (str3 < str4)
        cout << "String 3 is Less than String 4.\n";
    else
        cout << "String 3 is NOT Less than String 4.\n";
    MyString str5 = str1 + str2;;
    cout << "\n\n\nStr5:\t" << str5 << endl;
    cout << "Str5[7]:\t" << str5[7] << endl; 
    str5[7] = '$';

    cout << "\n\nStr5:\t" << str5 << endl;

    cout << "\n\n\nstr5(5, 10):\t" << str5(5, 10) << endl;

    cout << "\n\n ++Str3 :\t" << ++str3 << endl;

    str5 = str4 - str3;

    cout << "\n\n Str4 - Str3 :\t" << str5 << endl;

    cout << "\n\n --Str3 :\t" << --str3 << endl;

    cout << "\n\n --Str3 :\t" << --str3 << endl;

    str5 = str4 - str3;

    cout << "\n\n Str4 - str3 :\t" << str5 << endl;

    return 0;
}

Problem:

The lines str3 = str1 + str2; and MyString str4 = str1 + str2; will give an error that no operands matches. This error starts from Visual Studio 2019 and onwards versions.

But, when I use const in both copy constructor and assignment operator (such as MyString(const MyString&); MyString& operator=(const MyString&);), both lines (str3 = str1 + str2; MyString str4 = str1 + str2;) looks fine to compiler.

But if I Write str3 = str1; and MyString str4 = str2; now there is no need to write const in the copy constructor and assignment operator.

Why is the compiler showing this behaviour.?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Probably a dup of https://stackoverflow.com/q/16380966/4641116 — use `/Za` in VS 2012 to make the bug in the code an error. – Eljay Apr 22 '22 at 12:32
  • @Eljay I don't think it's a duplicate, though the post you linked is relevant. As I see it, the OP doesn't 'get' why the `a = b + c` construct can't be used (without the `const`) but that a simple `a = b` can be. – Adrian Mole Apr 22 '22 at 12:59

1 Answers1

4

The assignment operator that you have defined (i.e., taking a non-const reference) requires that its argument be (or be convertible to) an lvalue reference because it is non-const.

However, the right-hand side of the assignment operator in the expression, str3 = str1 + str2 is the result of the addition, which is a temporary value and, as such, is a prvalue that cannot be (implicitly) converted to an lvalue.

Adding the const qualifier to the parameter allows that operator to accept rvalue references (because you are explicitly stating that the referred-to value will not be modified).

This is why, generally, copy constructors and copy assignment operators are defined with const &T parameters (though they don't have to be).

For a fuller discussion of the differences between lvalues and rvalues, see here: Exact difference between rvalue and lvalue.

On why/how this "binding of a temporary to an lvalue reference" was allowed in the earlier version(s) of Visual Studio, see: Non-const reference bound to temporary, Visual Studio bug?.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • The OP asks about the change of behavior among different versions of VS. I guess you may need to point to compiler comformance to C++11/14/17 as well. – Red.Wave Apr 23 '22 at 11:03
  • My question is that why other compilers and specially visual studio 2012-2017 versions allow to write the code without using const in copy constructor and assignment operator while visual studio 2019 onwards this is not allowed? – Muhammad Daniyal Tahir Apr 25 '22 at 19:43
  • @MuhammadDaniyalTahir It's a 'feature' (a.k.a. "bug") in the earlier versions of MSVC. See my last link. – Adrian Mole Apr 25 '22 at 20:52