Search code examples
c++typedefforward-declarationeffective-c++

How to understand the typedef in this declaration


Recently, I read the book Effective C++ and there is a declaration about typedef in Item 35 which confuses me.

class GameCharacter; // Question1: Why use forward declaration?

int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter{

    public:
        typedef int (*HealthCalcFunc)(const GameCharacter&); // Question 2: What does this mean?

        explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
            : healthFunc(hcf)
        {}

        int healthValue() const
        {return healthFunc(*this); }

    private:
        HealthCalcFunc healthFunc;
};

So my first question is: Why does the author use a forward declaration here? Is there any specific reason?

And my second question is: How do I understand the typedef declaration, and how do I use it? I just know something like typedef int MyInt;


Solution

  • So my first question is that why the author use forward declaration here?

    So that the compiler knows that GameCharacter is a valid name when the int defaultHealthCalc(const GameCharacter& gc); line is encountered.

    And my second question is how to understand the typedef declaration and how to use it?

    Ideally, you do not use it anymore.

    Starting with C++11, using is a superior alternative because it's more readable; unlike typedef'ed function pointers, it clearly separates the name from that which the name describes. Compare this:

    typedef int (*HealthCalcFunc)(const GameCharacter&);
    

    With this:

    using HealthCalcFunc = int (*)(const GameCharacter&);
    

    In the typedef version, the name HealthCalcFunc is surrounded on both sides by that which the name describes. This hurts readability.


    But the code can still be improved, because C++11 also introduced std::function as an alternative to and/or an abstraction layer above function pointers.

    using HealthCalcFunc = std::function<int(const GameCharacter&)>;
    

    This is so readable it hardly has to be explained at all. HealthCalcFunc is a function which returns an int and takes a const GameCharacter&.

    The defaultHealthCalc function fits into this definition. Here's a full example:

    #include <functional>
    
    class GameCharacter;
    
    int defaultHealthCalc(const GameCharacter& gc);
    
    class GameCharacter {
    public:
          using HealthCalcFunc = std::function<int(const GameCharacter&)>; 
    
          explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
           : healthFunc(hcf)
          {}
          int healthValue() const
          {return healthFunc(*this); }
    private:
           HealthCalcFunc healthFunc;
    };
    

    The great thing about std::function is that you are not restricted to free-standing functions. You can pass every function-like thing. Here are some examples:

    struct Functor
    {
        int f(const GameCharacter& gc);
    };
    
    int main()
    {
        // normal function:
        GameCharacter character1(defaultHealthCalc);
    
        // lambda:
        GameCharacter character2([](auto const& gc) { return 123; });
    
        // using std::bind:
        Functor functor;
        using namespace std::placeholders;
        GameCharacter character3(std::bind(&Functor::f, functor, _1));
    }
    

    See also Should I use std::function or a function pointer in C++?.