3

Ok, I know that operator. is not overloadable but I need something like that.

I have a class MyClass that indirectly embeds (has a) a std::string and need to behave exactly like a std::string but unfortunately cannot extend std::string.

Any idea how to achieve that ?

Edit: I want that the lines below to compile fine

MyClass strHello1("Hello word");

std::string strValue = strHello1;
const char* charValue = strHello1.c_str();

12
  • 2
    You have two options: inheritance, or making a lot of wrapper functions... Commented Oct 18, 2011 at 14:48
  • 1
    You want to do A given that you can't do A. Nice. Why can't you derive from std::string? Commented Oct 18, 2011 at 14:52
  • 2
    @Tomalek, for one, std::string does not have a virtual destructor. It is not meant to be used as a base class. Commented Oct 18, 2011 at 14:54
  • const charValue = is a pretty strong requirement ... Commented Oct 18, 2011 at 15:01
  • @MichaelGoldshteyn: Unless you are in the position to have a polimorphic deletion of a string, the fact that the destructor isn't virtual does not mean it cannot be derived. It means only that you cannot call delete on a string*. Commented Oct 18, 2011 at 15:02

6 Answers 6

1

As per your later edit, that:

MyClass strHello1("Hello word");

std::string strValue = strHello1;
const charValue = strHello1.c_str();

should compile fine, the only solution is to write a wrapper over std::string:

class MyClass
{
private:
   std::string _str;
public:
   MyClass(const char*);
   //... all other constructors of string

   operator std::string& ();
   //... all other operators of string

   const char* c_str();
   //... all other methods of string

   friend std::string& operator + ( std::string str, Foo f);
};

//+ operator, you can add 2 strings
std::string& operator + ( std::string str, Foo f);

This however is tedious work and must be tested thoroughly. Best of luck!

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

2 Comments

there are more functions than c_str and operator+. maybe metaprogramming may help ...
@cprogrammer I know, therefore the comments "all other..."
1

You can overload the conversion operator to implicitly convert it to a std::string:

class MyClass
{
    std::string m_string;
public:
    operator std::string& ()
    {
        return m_string;
    }
};

7 Comments

That would make a copy, though, which may not be what the OP needs.
Hm.. a reference would be okay, I guess. Updated.
this is not helpfull for std::string strValue = strHello1;
returning a non-const reference exposes encapsulated data to the outside world
I don't think this solves the problem of calling c_str(), as MyClass a; a.c_str(); will not even consider conversions, but rather complain that c_str is not a member method of MyClass
|
1

If you're not interested in polymorphic deletion of a string*, you can derived from std::string, just getting the behavior of your class behaving "as-a" std::string. No more, no less.

The "you cannot derived from classes that don't have a virtual destructor" litany (if that's the reason you say you cannot extend it) it like the "dont use goto", "dont'use do-while" "don't multiple return" "don't use this or that feature".

All good recommendation from and for people that don't know what they are doing.

std::string doesn't have a virtual destructor so don't assign new yourcalss to a std::string*. That's it.

Just make your class not having a virtual destructor itself and leave in peace. Embedding a string and rewriting all its interface just to avoid inheritance is simply stupid. Like it is stupid writing dozens of nested if to avoid a multiple return, just as its stupid introduce dozens of state flags to avoid a goto or a break. There are cases where the commonly considered weird things must be used. That's why those things exist.

4 Comments

you are right, except I cannot extend from std::string because I don't know what is my value. I need o make a look-up before deciding that. This is the reason MyClass exists.
@cprogrammer: sorry, but in your post there is no way to understand that. What do you mean with "I don't know what is my value"? a std::string always has a value (in fact it IS a [like a] value). If you want to behave as a std::string, your class itself will behave as a value.
"I have a class MyClass that indirectly embeds (has a) a std::string and need to behave exactly like a std::string"
@cprogrammer: this comment doesn't clarify the question. See my other answer.
1

You can extend an interface without publically deriving from it. This prevents the case of problematic polymorphic destruction (aka no virtual destructor):

#include <iostream>
#include <string>
using namespace std;

class MyClass
   : private std::string
{
public:
   MyClass(const char* in)
      : std::string(in)
   {}

   // expose any parts of the interface you need
   // as publically accessible
   using std::string::c_str;
   using std::string::operator+=;
};

int main()
{
   MyClass c("Hello World");

   cout << c.c_str() << "\n";

   c += "!";

   cout << c.c_str() << "\n";

   // Using private inheritance prevents casting, so 
   // it's not possible (easily, that is) to get a 
   // base class pointer

   //  MyClass* c2 = new MyClass("Doesn't work");
   // this next line will give a compile error 
   //  std::string* pstr = static_cast<MyClass*>(c2);
   // delete c2;
}

Comments

1

Trying to sum-up all the discussions, it looks like you will never find a suitable dolution because ... your requirements are in cotraddiction.

  1. You want you class to behave as ats::string
  2. std::string behave as a value but
  3. a value does not "change" when accessed but (think to c = a+b: do you expect a and b to change their value ??) ...
  4. when accessing your "value" you want to change it.

If what I summed up collecting all the peaces (suggestion: edit your question otherwise all the answerer will risk to be lost) is correct you are in trouble with 3 & 4 (note the 3. derives from the concept of "assignment", and you can do noting to change it, while 4. comes from you directly)

Now, I can try to reverse-engineer your psychology (because you didn't provide good information about what your class represent and why you cannot "extend" a string) I can find two possibility neither of which will fit all the above requirements.

  1. Store a string into your class and make your class to behave as a std::string. There are threee ways to come to this point:

    a. embed a string and make your class to decay to it: essentially your class must contain

.

operator std::string&() { return my_string; } //will allow l-value assignments
operator const std::string&() const { return my_string; } //will allow r-value usage
const char* c_str() const { return my_string.c_str(); }

b. derive from std::string. In fact that's like having an anonymous my_string in it, which "decay" operations implicit. Not that different as above.

c. embed or privately derive, and rewrite the std::string interface delegating the std::string functions. It's just a long typing to get exactly the b. result. so what the f..k? (this last question is dedicated to all the ones that believe a. or b. will break encapsulation. c. will break it as well, it will only take longer!)

  1. Don't store a string, but just get it as a result from a calculation (the "lookup" you talk about, not clarifying what it is).

In this last case, what you need is a class that decay automatically into std::string as a value. So you need, in your class

operator std::string() const { return your_lookup_here; }

note that const is necessary, but there is no need to modify your class inner state, since the resulting string is not stored.

But this last solution has a problem with const char*: since the string is temporary, until you don't assign it, its buffer is temporary as well (will be destroyed) so decaying into const char* to assign the pointer is clueless (the pointer will point to a dead buffer). You can have a

const char* c_str() const { return std::string(*this).c_str(); } //will call the previous one

but you can use this only into expressions or a pass-through parameter in function calls (since the temporary will exist until the evaluating expression is fully evaluated), not in an assignment towards an l-value (like const char* p; p = myclassinstace.c_str(); )

In all of the above cases (all 1.) you also need:

 myclass() {}
 myclass(const std::string& s) :my_string(s) { ... }
 myclass(const char* s) :my_string(s) { ... }

or - in C++11, just

template<class ...Args>
myclass(Args&&... args) :my_string(std::forward<Args...>(args...)) {}

In case 2., instead of initialize the not existent my_sting, you should use the arguments to set what you will look up (we don't know what it is, since you did not tell us)

Hope in all these option you can find something useful.

1 Comment

requirements are valid. maybe are not clear enough. I have a class the embeds a boost::flyweight<std::string> and I want that class to behave like a std::string. Anyway there is no solution to this except a wrapper (by hand or meta) over looked-up string. Regarding your answers 1a) is (indirectly) embedded already but that doesn't expose the functions to myClass. It only expose a conversion (myClass.c_str() is not valid). b) derive from std::string - not an option (the post: "I have a class MyClass that indirectly embeds (has a) a std::string")
0

You can add an operator const reference to string, as follows:

class C
{
public:
  // Implicit conversion to const reference to string (not advised)
  operator const std::string &() const
  {
    return s_;
  }

  // Explicit conversion to a const reference to string (much better)
  const std::string &AsString() const
  {
    return s_;
  }

private:
  std::string s_;
};

As you can see from the comments, it would be much better to add an explicit conversion function if you really need this behavior. You can read about why this is bad in the Programming in C++ Rules and Recommendations article.

4 Comments

Except that this operator won't be called when the C object is to the left of a ..
Why const? Would it still behave like a std::string if the operator is const?
@Luchian, Which const are you referring to? If you mean why is the member function const, it is beacuse it does not change any member variables. If you mean why is the string const, it is because it would be bad to expose encapsulated data to changes outside of the class.
@MichaelGoldshteyn operator const std::string &() const - both of them

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.