You can do it by putting extern
in front of the type (because extern is a storage class and class MyClass
is the type):
extern class MyClass myObject;
The above directly answers the original question about syntax. However, since there's apparently some confusion about this, I thought it's worth expending my answer.
First, relevant excerpts from the standard (n3690):
§ 3.1.2:
A declaration is a
definition
unless it declares a function without specifying the function’s body (8.4), it
contains the
extern
specifier (7.1.1) or a
linkage-specification
25
(7.5) and neither an
initializer
nor a
function-
body
, it declares a static data member in a class definition (9.2, 9.4), it is a class name declaration (9.1), it is
an
opaque-enum-declaration
(7.2), it is a
template-parameter
(14.1), it is a
parameter-declaration
(8.3.5) in a
function declarator that is not the
declarator
of a
function-definition
, or it is a
typedef
declaration (7.1.3),
an
alias-declaration
(7.1.3), a
using-declaration
(7.3.3), a
static_assert-declaration
(Clause 7), an
attribute-
declaration
(Clause 7), an
empty-declaration
(Clause 7), or a
using-directive
(7.3.4).
§ 3.2.5:
[Note:
The rules for declarations and expressions describe in which contexts complete class
types are required. A class type
T
must be complete if:
— an object of type
T
is defined (3.1), or
— a non-static class data member of type
T
is declared (9.2), or
— T
is used as the object type or array element type in a
new-expression
(5.3.4), or
— an lvalue-to-rvalue conversion is applied to a glvalue referring to an object of type
T
(4.1), or
— an expression is converted (either implicitly or explicitly) to type
T
(Clause 4, 5.2.3, 5.2.7, 5.2.9, 5.4),
or
— an expression that is not a null pointer constant, and has type other than
cv
void*
, is converted to
the type pointer to
T
or reference to
T
using a standard conversion (Clause 4), a
dynamic_cast
(5.2.7)
or a
static_cast
(5.2.9), or
— a class member access operator is applied to an expression of type
T
(5.2.5), or
— the
typeid
operator (5.2.8) or the
sizeof
operator (5.3.3) is applied to an operand of type
T
, or
— a function with a return type or argument type of type
T
is defined (3.1) or called (5.2.2), or
— a class with a base class of type
T
is defined (Clause 10), or
— an lvalue of type
T
is assigned to (5.17), or
— the type
T
is the subject of an
alignof
expression (5.3.6), or
— an
exception-declaration
has type
T
, reference to
T
, or pointer to
T
(15.3).
— end note]
Now, consider the first example from the question:
class MyClass MyObject;
/* ... */
class MyClass {} MyObject;
This is not valid, because the first line is both declaration and definition, per § 3.1.2).
The third example is valid, though:
// Header.hpp
class MyClass;
extern MyClass MyObject;
// Source.cpp
class MyClass {} MyObject;
This is valid, because the second line in Header.hpp
is only a declaration (§ 3.1.2), and MyClass
does not have to be a complete type at this point (the first bullet point in § 3.2.5).
The problem with the second example from the answer is simply incorrect syntax. As I wrote above, the correct way to do this is:
// Header.hpp
extern class MyClass myObject;
// Source.cpp
class MyClass {} myObject;
@Samer Tufail and @Frax suggested that this is useless. Maybe it's not very useful, but I can think about the following use cases:
Forward declaring the type in order to reduce number of included header. The class can be defined in another header, included only when client code needs to access members of a global object.
Being able to define a fixed number of global objects, while hiding class definition (in a source file). Such objects could be manipulated using free functions.
Wrapping a global object (of class type) in C API:
Example of 3.:
// header.hpp
struct MyClass
{
explicit MyClass(std::string s);
const char* getString() const { return m_str.c_str(); }
private:
std::string m_str;
};
extern "C"
{
MyClass myObject("some string");
const char* getString(const MyClass* obj) { return obj->getString(); }
}
// header.h (C API)
extern struct MyClass myObject;
const char* getString(const MyClass* obj);
There are other ways to do these things, but using forward declared type is possible. IMHO @Cryptoman asked about rarely used, but interesting feature, and there's no reason for downvoting the question.