3

I'm writing a String class. I'd like to be able to assign my strings such as;

a = "foo";
printf(a);
a = "123";
printf(a);
int n = a; // notice str -> int conversion
a = 456; // notice int -> str conversion
printf(a);

I've already assigned my operator=() method for string to integer conversion. How can I declare another operator=() so that I can do the reverse method?

When I declare another, it seems to override the previous.

String::operator const char *() {
    return cpStringBuffer;
}
String::operator const int() {
    return atoi(cpStringBuffer);
}
void String::operator=(const char* s) {
    ResizeBuffer(strlen(s));
    strcpy(cpStringBuffer, s);
}
bool String::operator==(const char* s) {
    return (strcmp(cpStringBuffer, s) != 0);
}

//void String::operator=(int n) {
//  char _cBuffer[33];
//  char* s = itoa(n, _cBuffer, 10);
//  ResizeBuffer(strlen(_cBuffer));
//  strcpy(cpStringBuffer, _cBuffer);
//}
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
kvanbere
  • 3,289
  • 3
  • 27
  • 52
  • Note: this "str -> int conversion" and the opposite, probably does not do what you think it does. – Kiril Kirov Aug 09 '12 at 12:37
  • This isn't homework. I was inspired to make a String class that mimics functional language strings because I got sick of doing the conversions myself. – kvanbere Aug 09 '12 at 12:39
  • I don't see what automatic conversion have to do with a functional language (Haskell will scream at you, when you try something like this.) And why do conversions yourself? There are conversion functions. – pmr Aug 09 '12 at 12:41
  • 2
    @kvanberendonck does it mimic **functional** languages??? Actually it mimic languages with dynamic type system (and if it's what you need then you should consider to use ANOTHER language instead of C++ because often type-safety is his reason to be). – Adriano Repetti Aug 09 '12 at 12:41
  • I do not know c++ so maybe I should not answer, but it seems to me that `a = 456` would be possible but not `int n = a`. If you declare an equal operator, then `foo = bar` would (i think) always use the = operator of the object on the left hand side (i.e. foo). So, to have `int n = a`, you would need to have an int class which defines an = operator with a string as a parameter. – Alderath Aug 09 '12 at 12:42
  • I put up the code I'm using. The one that's commented out is the one I'd like to work. Reading the current replies and messing around with them as we speak. – kvanbere Aug 09 '12 at 12:43
  • 1
    @Alderath: It can be done, and has in the question, by providing implicit conversions The `operator int()` defined above will be called when a `String` is used in a context where an `int` is required (handwaving here), but also in many contexts where it could be unwanted (where an `int` *could* be used) – David Rodríguez - dribeas Aug 09 '12 at 12:46
  • @kvanberendonck: What is the problem with the code you have commented out? (the problems with the design are multiple, but what is wrong with that particular implementation?) – David Rodríguez - dribeas Aug 09 '12 at 12:47
  • The problem I have is that when I put the second operator=() it seems to cancel out the first. Also, I tried adding methods for String::String(int) and such, but when I do `s=123;` for example, it says cannot convert String to int. – kvanbere Aug 09 '12 at 12:51
  • @DavidRodríguez-dribeas I thought he was trying to define an = operator which would be called for the object on the right hand side of the equal sign. But I learnt something today. Did not know about conversion operators before, and obviously, that's what's appropriate for one of the scenarios he described. – Alderath Aug 09 '12 at 12:53

4 Answers4

5

A single-argument constructor can act as an int->String conversion, whereas a so-called conversion operator does the converse int->String

class String
{
public:
    String(int)            {} // initialization of String with int
    String& operator=(int) {} // assignment of int to String

    operator int() const {} // String to int
};

Note however, that these conversions will happen implicitly and you can easily get bitten. Suppose you would extend this class to also accept std::string arguments and conversions

class String
{
public:
    String(int)          {} // int to String
    String(std::string)  {} // std::string to String

    // plus two assignment operators 

    operator int() const       {} // String to int
    operator std::string const {} // String to std::string
};

and you would have these two function overloads

void fun(int)         { // bla }
void fun(std::string) { // bla }

Now try and call fun(String()). You get a compile error because there are multiple -equally viable- implicit conversions. THat's why C++98 allows the keyword explicit in front of single-argument constructors, and C++11 extends that to explicit conversion operators.

So you would write:

class String
{
public:
    explicit String(int)          {} // int to String
    explicit operator int() const {} // String to int 
};

One example where implicit conversion might be legitate is for smart pointer classes that want to convert to bool or (if they are templated) from smart_pointer<Derived> to smart_pointer<Base>.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • I tried `String::String(int n)` and the line `s=123;` where s is the string seems to be an invalid conversion. Does it only work for the constructor? – kvanbere Aug 09 '12 at 12:53
  • With a constructor you could do either `String s(123);` or `String s = 123;`, but not `String s; s = 123;`. The latter is assignment, not initialization (so you would need to provide more assignment operators). – TemplateRex Aug 09 '12 at 12:54
  • Ah, thanks. Much clearer now. What I actually need to do is assignment, sorry for the misunderstanding :) – kvanbere Aug 09 '12 at 12:57
  • @kvanberendonck Having multiple assignment operators or constructors is not bad in itself, what is bad is having multiple implicit conversion operators from `String` to all those types as well. Best make those `explicit` or write member functions like `as_int()` – TemplateRex Aug 09 '12 at 13:03
4

Rather than assignment operators, you probably want conversion operators—there's no way you can define an additional assignment operator for int. In your String class, you might write:

class String
{
    //  ...
public:
    String( int i );           //  Converting constructor: int->String
    operator int() const;      //  conversion operator: String->int
    //  ...
};

You can add assignment operators in addition to the first, but they generally aren't necessary except for optimization reasons.

And finally, I think you'll find this a bad idea. It's good if the goal is obfuscation, but otherwise, implicit conversions tend to make the code less readable, and should be avoided except in obvious cases (e.g. a Complex class should have a converting constructor from double). Also, too many implicit conversions will result in ambiguities in overload resolution.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
2

To convert your class to the other you need conversion operator. Something like this:

struct Foo
{
    operator int() const //Foo to int 
    {
        return 10;
    }

    operator=(int val) //assign int to Foo
    {

    }

    operator=(const std::string &s) //assign std::string to Foo
    {

    }
};
Andrew
  • 24,218
  • 13
  • 61
  • 90
  • Thanks. What's the purpose of the operator=() then? Also, I don't think the above works if, for example, I need to convert an integer and store it in the string buffer rather than converting my class to another value type. – kvanbere Aug 09 '12 at 12:40
  • @kvanberendonck: operator=() is used to assign to Foo – Andrew Aug 09 '12 at 12:41
  • @kvanberendonck: The first (conversion) operator allows you to write `integer = string;`. The second (assigment) operator allows you to write `string = integer;`. – Mike Seymour Aug 09 '12 at 12:49
  • What if I need to use multiple `operator=(someType x);` How do I do that? – kvanbere Aug 09 '12 at 12:54
  • 1
    @kvanberendonck: you can overload `operator=` as any other function. I added one more operator to the example – Andrew Aug 09 '12 at 12:54
  • Got it working like this, thanks! One more thing, I can't seem to construct my operator=()'s without putting void before them. I get a type expected error. Is this normal? – kvanbere Aug 09 '12 at 13:01
  • @kvanberendonck: Like any function, they need a return type. You could return nothing (`void`), but it's conventional to return a reference to the object being assigned: `String & operator=(int x) {... return *this;}`. That allows users to chain assignments, `string1 = string2 = integer;`. – Mike Seymour Aug 09 '12 at 15:46
1

To enable int n = a (where a is an object of your string class) you need a conversion operator.

class string {
  public:
    operator int() const { return 23; }
};

To enable conversion to your type, you need a converting assignment and possibly a conversion constructor.

class string {
  public:
    string(int i);
    string& operator=(int i);
};

You will also need overloads for const char*, char* and so on.

pmr
  • 58,701
  • 10
  • 113
  • 156