1

so i ran along this question in a past test from my uni, and i noticed something strange that i couldn't find an explanation to.

the question is: what would the line cout << s2; in main would print?

I followed to flow execution in the VS2019 debugger and when the line MyString s2 = s1; was performed it called

operator const char* ()const { return str; }

and I couldn't figure why it would go there. After that the ctor would be called on s2.

I understand why the copy ctor wouldn't be called on this instance because it is explicit and in order to invoke it the line should have been MyString s2(s1); for example.

main:

 int main()
 {
    MyString s1;
    cout << s1;
    MyString s2 = s1;
    cout << s2;
    return 1;
}

The code:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

 class MyString
 {
     class MyChar
    {
         MyString * pStr;
         int index;
    public:
         MyChar(MyString * p, int i) : pStr(p), index(i) {}
         const MyChar & operator=(char c) {
             pStr->set(index, c); // call copy-on-write
             return *this;
            }
         operator char()const { return pStr->str[index]; }
    };

    friend class MyChar;
    char* str;
    int* ref_counter;
    void attach(const MyString & s) {
        ref_counter = s.ref_counter;
        str = s.str;
        ++(*ref_counter);
    }
    void detach() {
        if (--(*ref_counter) == 0) {
            delete[]str;
            delete ref_counter;
            
        }   
    }
    void set(int index, char c) {
        // this is the copy-on-write
        MyString temp(str);
        detach();
        attach(temp);
        str[index] = c;
    }
public:
    MyString(const char* s = "") : ref_counter(new int(1))
    {
         str = new char[strlen(s) + 1];
         strcpy(str, s);
    }
    explicit MyString(const MyString& s) { attach(s); } 
    ~MyString() { detach(); }
    const MyString & operator=(const MyString & s)
        {detach(); attach(s); return *this; }
    operator const char* ()const { return str; }        //operation in question
    MyChar operator[](int index) {
        return MyChar(this, index);
    }
    char operator[](int index)const {
        return str[index];
    }
    friend ostream & operator<<
        (ostream & out, const MyString & s) {
        return out << s.str << ", ref count = "
            << *s.ref_counter << endl;
    }
};

Hope someone can help me understand this transition

chemJam
  • 23
  • 4
  • 2
    Why is that surprising? There is a non-explicit conversion from `MyString` to `const char*`, and there is a non-explicit converting constructor that takes `const char*`. What did you expect to happen? – molbdnilo Jan 25 '21 at 13:36
  • If `explicit MyString(const MyString& s)` weren't `explicit` it would have been called instead, is that what you expected to happen? –  Jan 25 '21 at 13:39
  • Well, there's a perfectly valid copy constructor which would be even better. However, because they "just noticed accidently wrote s3 when it should be s2" indicates that the shown code is not the real code that exhibits this behavior, so who knows what the real code does. – Sam Varshavchik Jan 25 '21 at 13:39
  • This may be useful https://stackoverflow.com/questions/28010392/explicit-copy-constructor-call-syntax –  Jan 25 '21 at 13:42
  • @SamVarshavchik originally it was performed on two instances of MyString s2 and s3, doing the same operation. i figured 2 similar actions would be redundant since the outcome is similar. – chemJam Jan 25 '21 at 13:43

1 Answers1

4

The compiler tries hard to find a way to compile the program. Since it can't make an implicit copy (explicit copy ctor), it tries other constructors, and the const char* one seems to be a viable candidate after an implicit user-defined conversion to const char*. There are no other candidates, so it is selected.

Useful additional reads:

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 1
    Yes, as explained in "implicit conversions", the compiler will perform up to 3 conversions, including 1 user-defined one (a call), if needed to compile the code. Also note that `T x = y` is not an assignment but an initialization, so all constructors of `T` will be considered. – rustyx Jan 25 '21 at 13:58