5

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;

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lixun Bai
  • 191
  • 7

5 Answers5

14

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

Because the declaration of the function defaultHealthCalc uses const GameCharacter& as the type of the parameter, then GameCharacter needs to be declared in advance.

And my second question is how to understand the typedef declaration

It declares a type name HealthCalcFunc, which is a type of pointer to function, which takes const GameCharacter& as parameter and returns int.

and how to use it?

Just as the code sample showed,

explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) // declare a parameter with type HealthCalcFunc; 
                                                               // the default argument is function pointer to defaultHealthCalc
: healthFunc(hcf)                                              // initialize healthFunc with hcf
{}

int healthValue() const
{return healthFunc(*this); }                                   // invoke the function pointed by healthFunc

HealthCalcFunc healthFunc;                                     // declare a member with type HealthCalcFunc 
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
11

The forward declaration is needed because of the forward declaration of defaultHealthCalc, which uses GameCharacter. Without forward declaration, the compiler would not know that later, there will be a type named GameCharacter, and so you need to forward declare it, or move the definition before the function declaration.

That typedef means to name the type int(*)(const GameCharacter&) HealthCalcFunc. That's a function pointer to a int(const GameCharacter&). For that reason, typedefs are pretty unreadable, it is advised to use a using declaration instead:

using HealthCalcFunc = int(*)(const GameCharacter&);
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
9
typedef int (*HealthCalcFunc)(const GameCharacter&);

Declares a typedef named HealthCalcFunc for a function pointer type, where the function signature returns a int and takes a parameter of const GameCharacter&.

The forward declaration is needed because that class is used as parameter type in the typedef.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
9

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++?.

Community
  • 1
  • 1
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • _"Because the code can still be **improved**"_: mentioning `std::function` here is appropriate, but it is a fully different alternative, and for a simple example such as this one, it might be mis-leading to mention improvements when switching from a light weight function pointer type to the entirely different and, in comparison, "heavy" creature of `std::function`. Both have their place (mostly, imo, `std::function` should be preferred, but when working with older APIs ...), but are very different concepts, and whether to use one over the other should be weighted against the context. – dfrib Mar 12 '17 at 14:45
  • @dfri: I think `std::function` is much simpler to write, read and apply than a function pointer, so I consider it the standard solution, whereas function pointers are special-purpose beasts. – Christian Hackl Mar 12 '17 at 14:47
  • Yes I agree, note my ninja-edit. Still, when mentioning `std::function` as an always-improvement, it might be valuable to point out that there are situations where a simple function pointer is to prefer (or even the only alternative). Anyway, good answer, just a minor byline by me above! – dfrib Mar 12 '17 at 14:51
  • 1
    @dfri: I've added a link to a question which examines the advantages and disadvantages of function pointers compared to `std::function`. – Christian Hackl Mar 12 '17 at 14:55
  • @ChristianHackl This one is the best! Thank you so much! – Lixun Bai Mar 14 '17 at 09:28
2

The forward declaration of GameCharacter is not strictly needed; the function declaration could have read:

int defaultHealthCalc(const class GameCharacter& gc);

which has the same effect as the original code. But it is considered more readable to have the forward declaration on its own line.

M.M
  • 138,810
  • 21
  • 208
  • 365