When should I use an elaborated type specifier?
Asked Answered
B

5

10

Is there a particularly good reason to choose to use an elaborated type specifier? For example, in certain circumstances, one is required to use the template or typename keywords to disambiguate a dependent template or type.

I can't think of any examples where this would occur for something such as an enumeration. Take the following code example:

enum Foo { A,  B };

void bar(Foo foo);
void baz(enum Foo foo);

Why might I choose to use the syntax baz() provides over bar() (or vice-versa)? Is there any ambiguous case?

Backhand answered 5/6, 2012 at 18:7 Comment(4)
Isn't the baz syntax just a carryover from old C style where you hadn't typedef'd the enum so you had to explicitly refer to it as enum? Like how you'd use struct Foo f; if you hadn't typedef'd the struct? In C++ the typedef is implicit in the enum/struct declaration so you don't have to state the type is an enum/struct maybe?Kondon
similar... #743199Dracula
in standardese, forward declarations employs elaborated type specifiers.Austronesia
template and typename are not parts of elaborated type specifiers.Compartmentalize
G
8

There are no reasons to use such specifiers, unless you are dealing with the situation when the name is hidden by name of a different "kind". For example, it is perfectly legal to declare a variable named Foo after the enum declaration, since, speaking informally, object names and type names live in independent "namespaces" (see 3.3/4 for more formal specification)

enum Foo { A, B };

int Foo;

After the int Foo declaration, your bar declaration will become invalid, while the more elaborate baz declaration will remain valid.

Girish answered 5/6, 2012 at 18:16 Comment(1)
That makes sense; I hadn't considered this case. Granted, I would punch anyone who does it, but I do like to know about these corner cases. Thanks! (And to the others too.)Backhand
P
3

Elaborated type specifiers are required for declaring user-defined types. One use case is to forward declare your types. In the unlikely event that you have a function with the same name as an enum you have visible in scope you may need to use the elaborated type specifier in the function declaration:

enum A { A_START = 0 };

void A(enum A a) {}

int main() {
   enum A a;
   A( a );
}
Pram answered 5/6, 2012 at 18:16 Comment(0)
D
2

One good reason to choose to use an elaborated type specifier is to forward declare structures or classes in headers. Given a header that defines the type

// a.h
struct foo {};

You can include that header to prototype your function

#include "a.h"
void foo(foo * ) ;

or use the elaborated type:

void foo(struct foo * ) ;

or, explcitly forward declaring using the elaborated type:

struct foo ;
void foo( foo * ) ;

Either of the two last ways can help avoid your headers gradually degenerating into a fully connected web, where including any single header pulls in all the rest (I work on a software product where that is sadly true, forcing you to rebuild the world after many sorts of changes that you could imagine would be logically isolated).

I understand that C++11 will also allow this sort of forward referencing for enum's, something not currently allowed in C++01 compilers.

Dovap answered 19/12, 2012 at 2:6 Comment(0)
H
0

An example that might come up is when you have the type and also a non-type element of the same name. By using the elaborated type specifier you can explicitly request the type:

struct foo {};
void foo(struct foo) {}
int main() {
   struct foo f;
   foo(f);
}

Without the elaborated type specifier, foo in main refers to void foo(struct foo), not to the type struct foo. Now, I would not like that to be in production code, but you only asked for an example where it matters. The same can happen if the type and the function (or variable) are defined in different namespaces where the non-type is found earlier by lookup. You can replace struct with enum above.

Hippel answered 5/6, 2012 at 18:16 Comment(0)
P
0

Forward declaration explained below is one of the manifestations of Elaborated Type Specifier.

In some object-oriented languages like C++ and Objective-C, it is sometimes necessary to forward-declare classes. This is done in situations when it is necessary to know that the name of the class is a type, but where it is unnecessary to know the structure.

In C++, classes and structs can be forward-declared like this:

class MyClass; 

struct MyStruct; 

In C++, classes can be forward-declared if you only need to use the pointer-to-that-class type (since all object pointers are the same size, and this is what the compiler cares about). This is especially useful inside class definitions, e.g. if a class contains a member that is a pointer (or a reference) to another class.

Forward-declaration is used to avoid unnecessary coupling which help reducing compilation time by reducing the number of header inclusion. This has a triple advantage:

  • reduce the number of files opened by #include (hence the number of operating system calls)
  • reducing the volume of the pre-processed files (as the header is not included)
  • reducing recompilation impact when the forward declared class is modified.

Forward declaration of a class is not sufficient if you need to use the actual class type, for example, if you have a member whose type is that class directly (not a pointer), or if you need to use it as a base class, or if you need to use the methods of the class in a method.

This allows you to use a pointer to the type without having to include the relevant header file in your header file (instead you only need it in the .cpp file).

This helps to reduce dependencies which can improve compile time.

It isn't always needed, particularly with Unreal Engine core classes as these are often included anyway. However it is good practice to use the prefix if you haven't explicitly included the header file. If you do this, if whatever file was including the header changes to not include it, your code will still compile.

While forward declarations are good, you should avoid ETSs. If you want to forward declare your types, it is best to do so explicitly at file scope:

Bad:

class Bad
 {
     void Func(class Y* YParam);
     class Z* ZProp;
 };
 

Good:

 class Y;
 class Z;
 class Good
 {
     void Func(Y* YParam);
     Z* ZProp;
 };
Pentheas answered 11/8, 2021 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.