1

Given

#include <string>
#include <iostream>


struct A  {
    virtual operator std::string() const { return "A"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

struct B1 : public A {
    virtual operator std::string() const { return "<"; }
};

struct B2 {
    B2() { }
    virtual ~B2() { }
    virtual operator std::string() const { return ">"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

struct C1 : public A {
    C1() { }
    virtual ~C1() { }
    virtual operator std::string() const { return "["; }
};

struct C2 {
    C2() { }
    virtual ~C2() { }
    virtual operator std::string() const { return "]"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

int main() {
    using namespace std;
    cout << B1() << endl;
    cout << C1();
    cout << C2() << B2() << endl;
}

The output should be "<[]>". However, it is "<[]]".

  1. Am I wrong? If so, why?
  2. If not, then what are the potential reasons for this behavior?
1
  • Don't think I have ever seen virtual applied to operator before. I am curious how you are going to invoke it. Commented Dec 16, 2011 at 15:01

2 Answers 2

5

Actually, the behaviour of your code is undefined due to the following:

return this->operator std::string().c_str();

You're calling c_str() on a temporary, and using the result later on.

What you're seeing is a valid manifestation of undefined behaviour.

Now, if you're curious as to what actually happens under the hood, you could modify the last line of main() to read:

    cout << (const void*)C2() << ' ' << (const void*)B2() << endl;

If you do, you'll probably see the same address getting printed twice (which in both cases is a dangling pointer). This is what happens on my computer, and I suspect what happens on yours. Of course, since the behaviour is undefined, this is just one possible manifestation of many.

Sign up to request clarification or add additional context in comments.

2 Comments

The only reason I was using the c_str() hack was because C++11 iostream is non-functional and it's refusing to cast to rvalues. Looks like my workaround will be shared_ptr<char>.
@moshbear: Or your strings/char* could be references to real objects. Casting sounds like you are doing something else wrong and shared_ptr seems like the completely wrong solution to this.
3

Your problem really boils down to:

#include <string>
#include <iostream>

struct B2 {
    B2() { }
    virtual ~B2() { }
    virtual operator std::string() const { return ">"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

struct C2 {
    C2() { }
    virtual ~C2() { }
    virtual operator std::string() const { return "]"; }
    virtual operator const char *() const { return this->operator std::string().c_str(); }
};

int main() {
    using namespace std;
    cout << C2() << B2() << endl;
}

Where it is evident that this has nothing to do with virtual functions. What happens here is that operator const char* is called, which calls operator std::string which returns a temporary std::string from which you return the .c_str(). Since after the call of operator const char* this temporary std::string is destroyed, you have a const char* pointing to already freed memory.

And now anything can happen, since this is UB...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.