CS 70

Classes Can Provide Type Conversion

  • LHS Cow speaking

    We just learned that C-style strings (which have type const char*) are automatically converted into std::string when we pass them into a function expecting a std::string.

  • Duck speaking

    Yes, and I want to know how to do that for my own classes!

  • Pig speaking

    Please tell us MORE!!

Conversion Into an Instance of Class

If a class provides a single-argument constructor, C++ will consider that constructor to be a way to automatically convert a value into that type.

  • Hedgehog speaking

    So it just happens automatically? No new thing to learn? No special syntax?

  • LHS Cow speaking

    That's right. If you write a single-argument constructor, you've automatically added type conversion, too.

For example, suppose we have a FuzzyBoolean class that supports the idea of Yes/No/Maybe and it has a constructor for FuzzyBool that takes a bool:

class FuzzyBool {
    // ...
    FuzzyBool(bool preciseBool);
    // ...
};

Also, suppose that we have written this function:

void makeDinner(FuzzyBool opinion) {
    if (opinion.positiveEnough()) {
        // make the dinner
    } else {
        // wait
    }
}

We can call makeDinner() with a FuzzyBool variable constructed using a boolean value as

FuzzyBool wantDinner{true};
makeDinner(wantDinner);

or we could call makeDinner() with a temporary FuzzyBool value from a boolean value as

makeDinner(FuzzyBool{true});

But we can also just say

makeDinner(true);

and C++ will implicitly make a temporary FuzzyBool object exactly as the previous code example did (explicitly) to automatically convert our true into a FuzzyBool object.

This automatic conversion occurs if makeDinner() takes the FuzzyBool by copy (as shown) or by constant reference.

But arguments that are passed by non-const reference (i.e., are writable) don't have automatic type conversion.

  • Duck speaking

    Why not? Why not have type conversion for all the things?

  • LHS Cow speaking

    It would probably be the wrong thing to do to create a temporary object, change it, and then just throw that object away. So to avoid surprising bugs, C++ doesn't do that.

  • RHS Cow speaking

    If we do want to do that for a function that writes to the argument, we have to do it by hand, and explicitly initialize a named variable like we did with wantDinner above.

Conversion Out of an Instance of Class

  • Duck speaking

    What if we wanted to go the other way, and automatically turn FuzzyBool objects into regular bools? We can't add a single-argument constructor to the bool class—it isn't even a class.

  • LHS Cow speaking

    Yes, there's a way to do that, too.

FuzzyBool can define a type-cast operator that defines how to turn it into a bool.

The type-cast operator is defined as a special member function (a type-conversion operator) whose name matches the name of the type you want to be able to convert to.

Declaration

class FuzzyBool {
    // ...
    operator bool();
    // ...
};

Definition

FuzzyBool::operator bool() {
    return state_ != NO;
};
  • Hedgehog speaking

    That looks pretty weird. It looks a lot like a function, but it doesn't have a return type.

  • LHS Cow speaking

    It does look a bit odd. The return type is already the function's name, so we don't have to say it twice.

  • RHS Cow speaking

    In any case, you won't need to write code like that in this class.

Avoiding Unintended Conversion

  • Cat speaking

    Having type conversion happen automatically without asking might not always be what I want. Can I turn it off?

  • LHS Cow speaking

    Yes!

The explicit keyword will ensure that a conversion constructor or type-cast operator is only used when you explicitly ask for it.

Explicit Parameterized Constructor

An explicit parameterized constructor will only be called in a context that looks like a constructor call.

In other words, if FuzzyBool has an explicit constructor that takes a bool,

class FuzzyBool {
    // ...
    explicit FuzzyBool(bool preciseBool);
    // ...
};

then

makeDinner(FuzzyBool{true});

will work, but

makeDinner(true);

will not.

Explicit Type-Cast Operator

An explicit type-cast operator will only be called if we explicitly ask for the conversion.

In other words, if FuzzyBool has an explicit type-cast operator bool(),

class FuzzyBool {
    // ...
    explicit operator bool();
    // ...
};

then

bool doit = bool(myFuzzyBool);

will work, but

bool doit = myFuzzyBool;

will not.

Limits on Conversion

Suppose we have a Person class with constructors,

Person(const string& name);
Person(int age);

and a Cow class with a member function,

Cow::addRider(const Person& rider)

What happens if bessie is a Cow and we say each of the following?

bessie.addRider(20);

string s = "Chris"; bessie.addRider(s);

bessie.addRider("Chris");

  • RHS Cow speaking

    For each argument to a function, the compiler will perform a single conversion.

  • LHS Cow speaking

    It won't even try to do multistep conversions; the potential chain of conversions would just be too long.

  • Duck speaking

    But… isn't this just a single conversion, from the string "Chris" to a Person named Chris?

  • LHS Cow speaking

    Remember that string literals are C-style strings. Type conversion turns them into std::strings.

A Place Where Type Conversion Doesn't Apply

There are lots of places where the compiler will implicitly convert types, but here's another place that it won't.

Suppose the Cow class has a feed member function, and a constructor that takes a std::string. So you can write

std::string s = "Bessie";
Cow bessie{s};
bessie.feed();

If you write

s.feed();

you will get a compiler error—the compiler won't transform the code into

Cow{s}.feed();  // You could write this; C++ won't.
  • LHS Cow speaking

    Why do you think that is?

  • Hedgehog speaking

    I don't know why, but I'm pretty glad it doesn't. It'd feel wrong somehow.

  • LHS Cow speaking

    Can we be more specific?

  • Cat speaking

    Well, we'd be feeding a temporary object and throwing it away, which probably isn't what we want.

  • LHS Cow speaking

    Indeed!

  • Dog speaking

    And to do that conversion, the compiler would have to look at every single class it knows about, see which one(s) can be created from a string, and then see which of those have a feed member function.

  • LHS Cow speaking

    Yes, that does seem a bit unreasonable, too.

(When logged in, completion status appears here.)