What are the pros/cons of this class definition style? - c++

I have seen following style class definition several times. What exactly are the pros/cons of this style?
typedef class _MyClass
{
public :
_MyClass();
} MyClass;

It's pretty rare in C++, but common in C (where struct Foo does not automatically alias to just Foo.) You might see it in a library that has different classes for different platforms (e.g. a "Canvas" class, which is very implementation-specific.) The library would use the typedef to let its users simplify their code:
#if WINDOWS
typedef class WindowsCanvas {
private: HWND handle;
} Canvas;
#elif MAC
typedef class MacCanvas {
private: CGContextRef context;
} Canvas;
#endif
In such an example, one would only need to use the Canvas type, never the platform-specific types.

In C++ there are no pros. This style came from C where you couldn't just use the struct's name as a type. E.g.
struct X
{
int x;
};
X a; //compiler error
struct X b; //OK
in order to avoid using the elaborated type specifier, like struct X a, or enum E e; etc. in C it is a common practice to typedef the name.
E.G.
typedef struct X_ { ... } X;
Now X a; is OK too.
Naturally in C++ there is no need to do this.

One possible advantage is illustrated by a highly contrived example, but since the Standard speaks of it, it must be having an impliciation for sure.
$7.1.3/6- "Similarly, in a given
scope, a class or enumeration shall
not be declared with the same name as
a typedef-name that is declared in
that scope and refers to a type other
than the class or enumeration itself.
[
typedef struct S{} MYS;
int MYS; // Error due to $7.1.3/6
struct A{};
int A; // No error, subsequent use required fully elaborated name
int main(){}

Related

Is a using declaration for a non-member type equivalent to an alias declaration with an identifier equal to the terminal name of the using declarator?

In short, I'm asking if doing
using foo::bar::baz;
has the same effect as
using baz = foo::bar::baz;
(Clearly I'm assuming that foo::bar::baz names a type that is not a class member, e.g. I'm referring to something like namesapce foo::bar { using baz = int; }, and not to something like namespace foo { struct bar { using baz = int; }; }.)
I'm pretty sure they are two different things (otherwise they could also always stay side by side, which is not the case, as I know that the former can't be in a struct/class, unlike the latter), but how do I read it from the standard? I was looking at [namespace.udecl] and [dcl.pre], but I can't really draw a conclusion.
There are definitely differences between the two, but most of them are of no interest to most programmers. For example,
namespace N {
struct X {};
struct Y {};
struct Z {};
}
namespace O {
int X,Y;
using N::X; // OK
struct X x; // OK, finds N::X
int i=X; // OK, finds O::X
using Y=N::Y; // error: conflicts with “int Y”
using Z=N::Z;
struct Z z; // error: elaborated-type-specifier with typedef-name
}
One practical distinction is in exporting a previously declared name in a module:
export module A;
export struct S {};
export struct T {};
export module B;
import A;
export using ::S; // OK
export using T=T; // error: conflicts with “struct T”
The using-declaration can of course also be applied to non-type declarations, whereas the alias-declaration can also be a template.

Why use 'struct' keyword in class pointer declaration in C++

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...
}
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;
}
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;
}
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.
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*);

enum type declared inside a class access confusion

In MyClass below, enum MyType is defined inside the class.
In main, I create a variable of MyClass::MyType t. This compiles fine. However, when I wish to assign it a value such as OPEN, there is a compilation error "OPEN was not declared in this scope"
Firstly it probably doesn't make sense declaring an enum type inside the class and limiting its scope there and then creating a variable of that enum type elsewhere, but I'm just trying to understand what's happening.
In the first place, how am I able to create a variable of MyType in main when an object hasn't even been created? Are enums and struct types defined in a class like that implicitly static?
Also, the compiler has access to the enum code, so why doesn't it understand "OPEN"? Thanks
#include <iostream>
using namespace std;
class MyClass
{
public:
enum MyType
{
OPEN,
CLOSED
};
struct MyStruct
{
int val1;
int val2;
};
};
int main()
{
MyClass::MyType t;
t = OPEN; // compilation error
return 0;
}
Your enum MyType is inside the class, so its values are expected to be accessed through the class and the enumeration. You are already creating a MyType without instantiating the class, but an example of instantiation through the class is also provided.
class MyClass
{
public:
enum MyType
{
OPEN,
CLOSED
};
struct MyStruct
{
int val1;
int val2;
};
};
int main()
{
MyClass::MyType t; // Already a MyType value!
MyClass c; // Building your class
t = MyClass::MyType::OPEN; // No compilation error
t = c.OPEN; // Accessing enum through instantiated class
return 0;
}
Like Remy said. Value OPEN is a part of MyClass class and is only reachable in that classes scope. For your compiler to see it and use it you need to acces it through MyClass::OPEN.
(In addition to what others wrote)
If supported by the compiler (Since C++ 11),
it is a better practice to use enum class:
enum class MyType
{
OPEN,
CLOSED
};
"The enum classes (“new enums”, “strong enums”) address three problems with traditional C++ enumerations:
1) Conventional enums implicitly convert to an integer, causing errors when someone does not want an enumeration to act as an integer.
2) Conventional enums export their enumerators to the surrounding scope, causing name clashes.
3) The underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible."
ISOCPP FAQ - enum class
-
In that case, use the syntax:
int main()
{
MyClass c;
MyClass::MyType t;
t = MyClass::MyType::OPEN;
return 0;
}
See [decl.enum]/11:
Each enum-name and each unscoped enumerator is declared in the scope
that immediately contains the enum-specifier. Each scoped enumerator
is declared in the scope of the enumeration. These names obey the
scope rules defined for all names in [basic.scope] and [basic.lookup].
[ Example:
enum direction { left='l', right='r' };
void g() {
direction d; // OK
d = left; // OK
d = direction::right; // OK
}
enum class altitude { high='h', low='l' };
void h() {
altitude a; // OK
a = high; // error: high not in scope
a = altitude::low; // OK
}
— end example ] An enumerator declared in class scope can be referred
to using the class member access operators (​::​, . (dot) and ->
(arrow)), see [expr.ref]. [ Example:
struct X {
enum direction { left='l', right='r' };
int f(int i) { return i==left ? 0 : i==right ? 1 : 2; }
};
void g(X* p) {
direction d; // error: direction not in scope
int i;
i = p->f(left); // error: left not in scope
i = p->f(X::right); // OK
i = p->f(p->left); // OK
// ...
}
— end example ]

Why do we use typedef in structure when we have a shorter method

When I define some random structure like for example in a cpp file in Visual studio
1) struct CAddition {
int x, y;
CAddition(int a, int b) { x = a; y = b; }
int result() { return x + y; }
};
and now if I define some structure object
CAddition foo;
it works without any error
but if I use any alias in the end
2) struct CAddition {
int x, y;
CAddition(int a, int b) { x = a; y = b; }
int result() { return x + y; }
}CAddition;
I cannot simply define any object without using struct before the definition
struct CAddition foo;
or an alternative method would be to add
typedef struct CAddition {
In the method 2 to avoid rewriting struct every time , My question is whats the difference between these 2 definitions , doesn't method 1 use less keywords and much easier to use in what conditions should we use the second definition of a structure.
With struct CAddition { ... } CAddition; you are doing two things:
You define the structure CAddition as a typename. That's what the struct CAddition thing does.
You define the variable CAddition. The variable is the one after the structure.
Because you define the variable CAddition you can not use that name for the type, as then the compiler will think you mean the variable and not the structure. To solve this you need to use struct CAddition to explicitly tell the compiler you mean the structure typename.
On an unrelated note: A struct is just like a class, with the difference being that all members a public by default in a struct. So you don't need the public specification in a struct.
it works without any error but if I use any alias in the end
In 2) you are not using an "alias", but you create an instance of that class you just defined (structs and classes are the same in c++ btw). You then have an instance with a name that shadows the class name and as a consequence you need the struct CAddtion to tell the compiler that CAddition refers to the class not to the instance you created.

Forward declarations of unnamed struct

Bounty question: So, these two Foos aren't the same thing. Fine. The second form is given in a library. How do I forward-declare it given that I can't change it?
I always thought C and C++ allowed repeated declarations provided that there were no repeated definitions. Then I came across this problem when trying to write C++ code which extends a C library.
struct Foo;
typedef struct {} Foo;
This gives the following error:
'struct Foo' has a previous declaration as 'struct Foo'
I want to forward-declare, darn it! What's wrong here?
typedef-ing anonymous struct is a practice that pre-dates C++03 and is mainly oriented to retain compatibility with pre-C99 compilers.
Given that this is 2011, and that both C++ and C are changed, I wonder why there is no more up-to-date version of such a library!
If it is not in development anymore, you cannot "leave", but just "survive" and change it is the way to do that.
If still in deployment, submit the issue to the development team.
If you need a workaround, consider that struct can inherit.
So, write a forward declaration like
struct MyFoo;
and define it as
#include "old_library.h"
struct MyFoo: public Foo {};
And in all your code, forget about Foo and always use MyFoo.
You're declaring two different entities with the same name. The first, struct Foo, is a struct named Foo. The second is an alias for an anonymous struct.
If you do instead:
struct Foo;
struct Foo {};
It works, because you're declaring a struct named Foo in both situations.
You cannot forward declare anonymous structs. You're left with two choices: include the whole definition, or change the header and name the struct.
In a similar situation, I have a legacy C header with something like
== old_library.h ==
typedef struct { int data; } Foo;
== /old_library.h ==
I use it in a C++ class of my own, as parameter for a private method:
class C {
void run(Foo *ds);
...
}
To avoid #include "old_library.h" from C.hpp, I use the following forward declaration:
class C {
struct Foo;
void run(Foo *ds);
...
}
and in C.cpp I have the following statements:
extern "C" {
#include "old_library.h"
}
struct C::Foo : public ::Foo {};
This way, I use C::Foo instead of Foo transparently, and don't need a MyFoo!
You don't need to typedef structs in C++:
struct Foo; // Forward declaration
struct Foo
{
}; // Definition
If you want to call it just Foo instead of struct Foo in C, you do need the typedef, which can also be done in different ways:
struct Foo; /* Forward declaration */
struct Foo /* The name is needed here */
{
}; /* Definition */
typedef struct Foo Foo; /* typedef */
or
struct Foo; /* Forward declaration */
typedef struct Foo /* The name is needed here */
{
} Foo; /* Definition and typedef combined */
You can of course use the form struct Foo in both C and C++.
Your forward declaration declares that there will be a struct called Foo.
Your second declaration is of a typedef called Foo. These are not the same thing.
For a forward declaration of a typedef, you need to refer to the thing that is being typedeffed, so like:
struct foo;
typedef foo bar;
class foo{};
Since you want to forward declare an anonymous struct, you can neither give it a name in the forward declaration of the original entity, nor can you refer to it when typedefing it. The "logical" syntax would be:
struct ;
typedef bar;
class {};
But since this is obviously not possible, you can not forward declare anonymous structs.
To go standardese, lets have a look at 9.1-2:
A declaration consisting solely of class-key identifier; is either a
redeclaration of the name in the current scope or a forward
declaration of the identifier as a class name. It introduces the class
name into the current scope.
No identifier, no forward declaration.
Bottom line of this: avoid anonymous structs unless they give you an advantage that you really need.
Your specific compiler may make a difference here.
Using MinGW GCC 3.4.5, both declarations compile with no errors or warnings (using -Wall)
struct Foo;
typedef struct {} Foo;
and
struct Foo;
typedef struct Foo {} Foo;
Is it possible a forward-declaration already exists inside the library? For instance, to allow a circular pointer:
struct Foo;
typedef struct Foo {
struct Foo *fooPtr;
} Foo;
If this already exists within the library headers, it would cause the error you describe.
IMHO, simply change your typedef to,
typedef struct Foo {} Foo;
^^^^^
There is no harm and it will still compatible in both C & C++. Now you can forward declare it.
[Note: If you still insist on on not touching the typedef at all then here is the dirty trick.
struct Foo;
#define struct struct Foo
#include"Foo.h" // contains typedef struct {} Foo;
#undef struct
This will work, only if Foo.h contains only 1 struct declaration. I don't recommend it.]
I think I recently ran into the same problem as the original poster, and have resolved it as below:
I am writing a wrapper around a third-party provided API defined as:
Foo.h:
typedef struct _foo
{
int i;
} Foo;
Foo* MakeFoo();
int UseFoo(Foo* foo);
My wrapper needs to have Foo as a member, but I don't want to expose Foo to all consumers of my wrapper.
UberFoo.h:
#pragma once
struct _foo; // forward declare the actual type _foo, not the typedef Foo
class UberFoo
{
public:
UberFoo();
int GetAnswer();
private:
_foo* f;
};
UberFoo.cpp:
#include "UberFoo.h"
#include "Foo.h"
UberFoo::UberFoo()
{
this->f = MakeFoo();
}
int UberFoo::GetAnswer()
{
return UseFoo(f);
}
Now, the consumers of my class can instantiate it without having access to the actual definition of _foo/Foo. This would also work if I needed to pass pointers to _foo as parameters or return them from functions as well as having a member _foo.
main.cpp:
#include <cstdio>
#include "UberFoo.h"
int main(int argc, char* argv[])
{
UberFoo u;
printf( "The Answer = %d\n", u.GetAnswer());
return 0;
}
The trick here was to forward declare the actual struct type, not the typedef'd name. Note that it is critical that the struct not be anonymous, as is common in older C code:
typedef struct // the actual struct type underlying the typedef is anonymous here
{
int i;
} ThisWontWork;
Hope this helps!
You can't forward declare it, since its unnamed. Its an unnamed struct, for which Foo is a typedef.
Why don't you just avoid forward decl.
If the second definition was in a header file, you could include the header file first in your C++ header file. To make C++ think it as C header, embracing #include with extern "C" { and } must be suffice in most case.
You are trying to typedef a previously used name. The statement is perfectly valid, only you need to use a different name.
struct Foo; // Forward declaration of struct Foo
typedef struct {} anotherFoo; // Another structure typedefed
struct Foo {
// Variables
}; // Definition of the forward declared Foo.
Note that the typedef cannot be used in the same name.