The C++ Programming Language (3rd Edition)

Author: Bjarne Stroustrup
4.3
All Stack Overflow 15
This Year Stack Overflow 2
This Month Stack Overflow 2

The C++ Programming Language (3rd Edition)

4.3

Review Date:

Comments

by anonymous   2017-08-20

This was my code posted as a suggested solution to c++ passing function into main error

Yes it needs a C++11 compiler as stated in the code. It was never meant as final code either.

I would recommend the OP to read a good book about C++. Let me suggest "C++ The Programming Language" http://www.amazon.com/The-Programming-Language-3rd-Edition/dp/0201889544

by Firas Assad   2017-08-20

Bruce Eckel's Thinking in C++ is how I learned about templates. The first volume has an introductory chapter and the second volume has an in-depth chapter on templates.

There's Bjarne Stroustrop's The C++ Programming Language which has a good chapter on them. And The C++ Standard Library: A Tutorial and Reference which is about the standard library, but would definitely help you get a better understanding of how templates could be used in the real world. .

by anonymous   2017-08-20

You're almost certainly referring to type fields as discussed in the book The C++ Programming Language by Bjarne Stroustrup. A type field in this context would simply be a variable of some kind in a base class that indicates the actual type of its subclasses. Here's an example:

class Pet
{
public:
    enum PetType { Dog, Cat, Bird, Fish };

    void ToString()
    {
        switch(type)
        {
        case Pet::Dog: std::cout << "Dog" << std::endl; break;
        case Pet::Cat: std::cout << "Cat" << std::endl; break;
        case Pet::Bird: std::cout << "Bird" << std::endl; break;
        case Pet::Fish: std::cout << "Fish" << std::endl; break;
        }
    }

private:
    PetType type; // A type field.
};

class Dog : public Pet
{
public:
    Dog() { type = Dog; }
};

// And so on...

void Test(const Pet& p) { p.ToString(); }
int main()
{
    Dog d;
    Test(d);
    return 0;
}

This is an extraordinarily brittle way to implement a ToString() method. Every time you need to add a derived class of Pet, you would need to update the PetType enumeration and the ToString() method. For example, if I need a Turtle subclass, I would need to make these changes:

// ...
enum PetType { Dog, Cat, Bird, Fish, Tutle /* Added */};
void ToString(const Pet& p)
{
    switch(p.type)
    {
    case Pet::Dog: std::cout << "Dog" << std::endl; break;
    case Pet::Cat: std::cout << "Cat" << std::endl; break;
    case Pet::Bird: std::cout << "Bird" << std::endl; break;
    case Pet::Fish: std::cout << "Fish" << std::endl; break;
    case Pet::Turtle: std::cout << "Turtle" << std::endl; break; // Added
    }
}
// ...
class Turtle : public Pet
{
public:
    Turtle() { type = Turtle; } // Added
};

Imagine if the Pet class had more functions than just ToString(); maintenence becomes a nightmare. It's lot of code one needs to change, but the important thing is that in order to have a Turtle class, I need to modify the Pet class. That means more testing, code review, etc. is needed. It's a clear violation of the open/closed principle. That's why type fields are extremely error-prone.

A significantly superior way would be to use virtual functions:

class Pet
{
public:
    virtual void ToString() = 0;
};

class Dog : public Pet
{
public:
    virtual void ToString() { std::cout << "Dog" << std::endl; }
};

class Turtle : public Pet
{
public:
    virtual void ToString() { std::cout << "Turtle" << std::endl; }
};

// And so on...

void Test(const Pet& p) { p.ToString(); }
int main()
{
    Turtle t
    // Will call Turtle::ToString(), even though
    // Test() was only given a const Pet&
    Test(t); 
    return 0;
}

Note that the above code requires no extra enums or switch statements. Calling Pet::ToString() will call the correct implementation of ToString() for Dogs, Cats, etc. automatically, with much less code. I don't even need to change the Pet class; I can just drop in a Turtle class if needed, provided that Pet has been defined.


For a possibly legitimate use of type fields, see this Stack Overflow question and the answers to that question.

by anonymous   2017-08-20

You second example fails because Scala needs a bit of help in establishing the type bounds in C. I suppose maybe it should be smart enough to figure it out on its own, but someone more versed in type theory would have to explain why or why not. This definition for C should work:

trait C extends A with B {
  type O >: Z2 <: Z // restrict O's type so it's valid for both A and B
}

Scala's handling of abstract types is similar to its handling of abstract methods. Methods of the same name (and signature) that are pulled in from multiple traits are overridden together. Take the classic counter-example, derived from an example given in Ye Olde C++ Programming Language.

trait Cowboy {
  /** Shoot something */
  def draw
}

trait Window {
  /** Draw something */
  def draw
}

class CowboyWindow {
  /** Probably best to draw something on the screen, then shoot it */
  def draw {
    // ...
  }
}

In the same way that it assumes that there's only one type O in your code, it assumes there's only one method draw in this code. In this instance, this assumption is problematic, since you could end up shooting yourself in the foot.

As you discovered in your example, usually this assumption works out for the best (think about multiple interfaces declaring a close() method). But if this assumption is problematic for some class, as it is for CowboyWindow, you can replace inheritance with composition to work around it.