3

When and why should we use the 'struct' keyword when declaring a class pointer variable in C++?

I've seen this in embedded environments so I suspect that this is some kind of hold over from C. I've seen plenty of explanations on when to use the 'struct' keyword when declaring a struct object as it relates to namespaces in C (here), but I wasn't able to find anyone talking about why one might use it when declaring a class pointer variable.

Example, in CFoo.h:

class CFoo
{
public:
    int doStuff();
};

inline Foo::doStuff()
{
    return 7;
}

And later in a different class:

void CBar::interesting()
{
    struct CFoo *pCFoo;

    // Go on to do something interesting with pCFoo...
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Jonathan
  • 67
  • 1
  • 8
  • 4
    @πάνταῥεῖ Once again, bad dupe. That one is about C. This question is about C++. The distinction is _literally the entire point of the question_. Please stop. – Lightness Races in Orbit Aug 21 '19 at 13:50
  • Closing this as a dupe to a C question isn't a good idea. In C this is purely a matter of style. – Lundin Aug 21 '19 at 13:50
  • @Lundin In C++ it's a matter of style. In C it's mandatory. – Lightness Races in Orbit Aug 21 '19 at 13:50
  • @LightnessRacesinOrbit Not if you use typedef, which is by far the most common style in C. – Lundin Aug 21 '19 at 13:51
  • @Lundin Well, that's cheating! This question is not about using type aliases, but about using the actual names of types and the limitations/constraints/requirements thereof. Yes, you can use a type alias to bypass said requirements in C. Yes, that's extremely common. Though, as a side note, things like `struct tm` are not given this treatment so they're extremely common too - even in C++ code lol – Lightness Races in Orbit Aug 21 '19 at 13:53

4 Answers4

7

There's rarely a reason to do this: it's a fallover from C and in this case the programmer is simply being sentimental - perhaps it's there as a quest for readability. That said, it can be used in place of forward declarations.

In some instances you might need to disambiguate, but that's not the case here. One example where disambiguation would be necessary is

class foo{};

int main()
{
    int foo;
    class foo* pf1;
    struct foo* pf2;
}

Note that you can use class and struct interchangeably. You can use typename too which can be important when working with templates. The following is valid C++:

class foo{};

int main()
{    
    class foo* pf1;
    struct foo* pf2;
    typename foo* pf3;
}
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
5

There are two reasons to do this.

The first one is if we are going to introduce a new type in the scope using an elaborated name. That is in this definition

void CBar::interesting()
{
    struct CFoo *pCFoo;

    // Go on to do something interesting with pCFoo...
}

the new type struct CFoo is introduced in the scope provided that it is not yet declared. The pointer may point to an incomplete type because pointers themselves are complete types.

The second one is when a name of a class is hidden by a declaration of a function or a variable. In this case we again need to use an elaborated type name.

Here are some examples

#include <iostream>

void CFoo( const class CFoo * c ) { std::cout << ( const void * )c << '\n'; }

class CFoo
{
public:
    int doStuff();
};


int main() 
{
    class CFoo c1;

    return 0;
}

Or

#include <iostream>

class CFoo
{
public:
    int doStuff();
};

void CFoo( void ) { std::cout << "I am hidding the class CGoo!\n"; }

int main() 
{
    class CFoo c1;

    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

In C, two different styles are the most common:

  • typedef struct { ... } s; with variables declared as s name;.
  • struct s { ... }; with variables declared as struct s name;

In C++ you don't need to typedef to omit the struct keyword, so the former style is far more in line with the C++ type system and classes, making it the most common style in C++.

But then there are not many cases in C++ when you actually want to use struct instead of class in the first place - structs are essentially classes with all members public by default.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • _"structs are essentially classes with all members public by default"_ [Classes are classes are classes.](https://stackoverflow.com/a/36917400/560648) [C++ does not have structs.](https://stackoverflow.com/a/34108140/560648) (+1 though) – Lightness Races in Orbit Aug 21 '19 at 13:55
  • @LightnessRacesinOrbit There was some point in time when STL functors were encouraged to be written as structs rather than classes, for some mysterious reason. Though that might very well have been in the pre C++98 days. – Lundin Aug 21 '19 at 13:58
  • I use `struct` to define functors, usually, because they don't _tend_ to have state so I only have to write `private:` rarely (as opposed to `public:` frequently) and frankly it just "looks more lightweight" (even though that's a lie). But they're still classes :) – Lightness Races in Orbit Aug 21 '19 at 14:12
  • @LightnessRacesinOrbit I think I picked it up once upon a time from the odd little book "Designing components with the C++ STL", published year 2000. The author (Breymann) gives this weird rationale for writing functors as structs: "The word struct saves the public label. Everything can be public, because the class has no data to be protected." Which is true, I suppose... and he called it a class even back then. – Lundin Aug 21 '19 at 14:25
  • Hmm, if I'm reading that right, his rationale is "everything can be public, so you can use a struct", as if to say "everything in a struct must be public". Thing is, that's not true. – Lightness Races in Orbit Aug 21 '19 at 14:43
2

The reason for this may be as simple as not having to include a header file whose contents aren't needed other than for announcing that CFoo names a type. That's often done with a forward declaration:

class CFoo;
void f(CFoo*);

but it can also be done on the fly:

void f(struct CFoo*);
Pete Becker
  • 74,985
  • 8
  • 76
  • 165