This code compiles (and appears to work) with both GCC and Clang:
#include <iostream>
struct Foo {
enum { number = 42 };
};
int main()
{
Foo bar;
std::cout << bar.number << std::endl;
}
See it here.
It was surprising to me that the compiler accepts bar.number; all text books I can find teach to use Foo::number to access the enumerated value.
Is this code valid? Note that GCC gives a weird warning ("variable 'bar' set but not used"), while Clang doesn't complain about it.
It's perfectly valid:
[C++11: 7.2/10]: 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 (3.3) and (3.4). [..] An enumerator declared in class scope can be referred to using the class member access operators (::, . (dot) and -> (arrow)), see 5.2.5.
The same text (minus rules for scoped enums) can be found in the C++03 standard, at the same location.
I have to admit, this surprises me somewhat as well. I'd have expected :: to be the only valid mechanism.
The warning you get in GCC is not about the use of the enum, but about the fact that you use nothing but the enum. In such a case, you'd typically write Foo::number and avoid instantiating a Foo instance.
Just a guess here:
Since the enum is technically a type, Foo::number could be treated as a const or static const by the compiler. What would happen if you:
#include <iostream>
using namespace std;
enum { number = 42 };
int main()
{
std::cout << number << endl;
}
In this case, number appears to have been resolved to an immediate, and is valid within both this-> and type. scopes.
As I said, this is just a guess. #LightnessRacesInOrbit has spec quotes. He/She/Insanely-well-informed Turing Machine wins.
Related
The C++14 (precisely, N4296) says with respect to enumerations, in 7.2:11:
Each enum-name and each unscoped enumerator is declared in the scope
that immediately contains the enum-specifier.
Now what happens if a namespace N contains an opaque-enum-declaration of an enum E, and later the enumeration is fully declared from the global namespace? Shall we find its enumerators in the global namespace, or in the namespace N?
Of course, in order to opaque-declare an unscoped enumeration, it shall have a fixed underlying type. Consider the following piece of code.
namespace N { enum E : int; }
enum N::E : int {A,B};
namespace N {
int foo() {
return int(::N::B);
}
}
int bar() {
//return int(::A);
return int(A);
}
The first line in bar is commented out, because clang++ -std=c++14 says:
no member named 'A' in the global namespace; did you mean simply 'A'?
Gcc fails to compile both lines in bar(). So both gcc and clang declare the enumerations in the namespace N.
So my questions are:
What is the scope that immediatelly contains the enum specifier? (I thing it is the scope of the global namespace).
Should the enumerators A, B be defined in the global namespace?
In the bar function, why ::A does not refer to the enumerator, but simple A does?
Why the expression ::N::B in the function N::foo denotes the enumerator?
EDIT 1: the original declaration was enum ::N::E : int {A,B};, but gcc was not able to parse it (bug report), so I removed the leading colons to use enum N::E : int {A,B};
EDIT 2: the clang's behavior is a bug
The enumeration E is declared within the namespace N, even though its definition is set within the global namespace. As such, it can only be accessed in the scope of N.
The bar function should then be defined as:
int bar() {
return int(N::A);
//SAME AS --> return int(::N::A);
}
In this code:
typedef int foo;
struct S
{
foo foo;
};
int main() {}
all versions of clang -std=c++14 accept this code, however all versions of g++ -std=c++14 report:
5 : error: declaration of 'foo S::foo' [-fpermissive]
foo foo;
^
1 : error: changes meaning of 'foo' from 'typedef int foo' [-fpermissive]
Is the code correct?
The code is wrong. typedef is a new name for a existing type. So you can not create a variable with a type's name like foo foo; is equal to int int.
g++ -std=c++14 is the correct one.
Also refer this question
According to the C++ standard, declaring a variable with the same name as a type is correct code in general, but invalid code within a class definition. The class case is specific, because names declared in a class definition are visible within the whole class definition, before and after the point of declaration of that name. In other scopes (global, namespace, function, ...) declared names are visible only after the point of declaration.
For the example given in the question: If you have another reference to foo before the member declaration foo foo;,
struct S { foo another_member; foo foo; };
to which foo should it refer? To the type or the member foo? In this case it would be possible to deduce from the context that the type is meant, but the C++ standard probably avoided complexity for treating corner cases.
But foo foo; is valid code outside of class definitions: Additionally to the excerpt from section [dcl.spec], which Serge Ballesta already citied in his answer, section [dcl.decl] is useful in this context. It gives a valid example for this case (in older versions of the C++ standard text in a note below the text, in later versions as part of the main text) - here from the N3242 draft (final draft for C++11):
A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That is
T D1, D2, ... Dn;
is usually equvalent to
T D1; T D2; ... T Dn;
where T is a decl-specifier-seq and each Di is an init-declarator.
The exception occurs when a name introduced by one of the declarators
hides a type name used by the decl-specifiers, so that when the same
decl-specifiers are used in a subsequent declaration, they do not have
the same meaning, as in
struct S ... ;
S S, T; // declare two instances of struct S
which is not equivalent to
struct S ... ;
S S;
S T; // error
The excerpt from section [dcl.spec] is useful as well, as it describes, when a name in a declaration of a variable is interpreted as type name and when as the variable name (long quote is given in Serge Ballesta's answer):
... it is interpreted as part of the decl-specifier-seq if and only if
...
The relevant section for the class' case, which is given in the original question, is [basic.scope.class]:
... A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in
the completed scope of S. No diagnostic is required for a violation of this rule. ...
This means that the code of the original question is invalid, but the compiler does not need to give an error. So both clang and gcc behave correctly (according to the C++ standard).
I would say CLang is correct here - even if I would never use this.
C++ 14 draft N4296 says in 7.1 Specifiers [dcl.spec] 3
If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-
seq if and only if there is no previous type-specifier other than a cv-qualifier in the decl-specifier-seq. The
sequence shall be self-consistent as described below. [ Example:
typedef char* Pc;
static Pc; // error: name missing
Here, the declaration static Pc is ill-formed because no name was specified for the static variable of type Pc.
To get a variable called Pc, a type-specifier (other than const or volatile) has to be present to indicate that
the typedef-name Pc is the name being (re)declared, rather than being part of the decl-specifier sequence.
For another example,
void f(const Pc); // void f(char* const) (not const char*)
void g(const int Pc); // void g(const int)
(emphasize mine)
Even if an example is not normative, it let think that for the writers of C++ specification, a variable can redeclare the name of a typedef.
But g++ is simply more conservative what looks more reasonable. If I ever see such a construct in production code, the programmer would soon learn not to do it again, even I the compiler accepted it...
I'm looking for a formal explanation of that fact in the Standard.
I've found what 3.9.1/9 says and trying to give an explanation used that section.
Section 3.9.1/9, N3797:
The void type has an empty set of values. The void type is an
incomplete type that cannot be completed. It is used as the return
type for functions that do not return a value. Any expression can be
explicitly converted to type cv void (5.4). An expression of type void
shall be used only as an expression statement (6.2), as an operand of
a comma expression (5.18), as a second or third operand of ?: (5.16),
as the operand of typeid, noexcept, or decltype, as the expression in
a return statement (6.6.3) for a function with the return type void,
or as the operand of an explicit conversion to type cv void.
I don't understand how it implies from the fact that the void type has an empty set of values?
Suppose that type T has an empty set of values. Why does compiler throw an error when it come across the following line:
extern T v;
We can decalre a variable of incomplete type in the following way:
#include <iostream>
#include <cstring>
using namespace std;
struct Foo;
extern Foo f; //OK!
int main()
{
}
and it works fine
DEMO
It cannot be done on a void type
#include <iostream>
#include <cstring>
using namespace std;
extern void f; //compile-time error
int main()
{
}
DEMO
You cannot declare a variable of type void because variables must have object type or be references, extern void f; doesn't declare a reference, and void is not an object type:
Section 3 [basic] says that
A variable is introduced by the declaration of a reference other than a non-static data member or of an object.
Section 3.9 [basic.types] says that
An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type.
"void type is an incomplete type"
You can't create variables of any incomplete type
"...that cannot be completed"
While your example of extern incomplete struct can be completed at some later point, the compiler knows that any declaration of type void can never be completed.
[edit] The answer below makes valid observations, but they're contradicting. As these might be valuable, I'll not delete them, but see Ben Voight's answer and the comments there for a more straightforward approach.
Your observations about extern declarations are specifically allowed by 7.1.1/8:
The name of a declared but undefined class can be used in an extern declaration. Such a declaration can only be used in ways that do not require a complete class type.
void is not a "declared but undefined class", and there's no other exception in 7.1.1 which applies.
Additionally, 3.9/5 is fairly explicit that it is in fact allowed:
A class that has been declared but not defined, an enumeration type in certain contexts (7.2), or an array of unknown size or of incomplete element type, is an incompletely-defined object type. [45] Incompletely defined object types and the void types are incomplete types (3.9.1). Objects shall not be defined to have an incomplete type.
Emphasis mine. This part of the standard is quite specific about the differences between definitions and declarations, so by omission it specifies that declarations are allowed.
If the variable has an empty set of values, it can't be used for anything.
You can't assign to it, because there are no possible values to assign.
You can't access it, because you never assigned to it, so it has an indeterminate value.
Since there are no possible values, there's no size of the variable.
void is just used as a placeholder in variable places. It's used as a return type to indicate that the function doesn't return a value. It's used in C in the argument list to indicate that the function takes no arguments (to resolve an ambiguity from the pre-prototype version of the language). And it's used with pointer declarations to create generic pointers that can be translated to any other pointer type. There's no such analogous use for it in variable declarations.
Because C and C++ assume that any objects may be compared for identity by comparing their addresses, they must ensure that all objects have fixed non-zero size. Were it not for that requirement, there are in fact many cases where it would be somewhat useful to declare zero-sized objects [e.g. in code which uses templates which contain fields that will sometimes be useful and sometimes not, or as a means of forcing a structure to be padded to a certain alignment requiring that it contain an element requiring such alignment]. As it is, however, zero-size types would be inconsistent with fact that the rule specifying that every object has a unique address includes no exception which would allow for the existence of zero-sized objects that could share an address.
Even if zero-size objects were permissible, however, a "pointer to unknown object" should not be the same as a "pointer to a zero-size object". Given that the type void* is used for the former, that would imply that something else should be used for the latter, which would in turn imply that something other than void should be the type of thing to which a zero-sized object points.
void is an incomplete type - you can only declare pointers to them and use them in function signatures. Obviously, extern Foo f; is permitted because struct Foo can be defined in another compilation unit (and if it's not the error will be detected by the linker), but void can't ever be "defined" (and the compiler knows this, of course) so void's quite special in this case.
Well - I really don't see the rationale behind this. It's going to be great if this way we can declare a variable with unknown type. Something like 'void *' and arrays of unknown size. Imagine code like this:
#include <iostream>
#include <cstring>
using namespace std;
extern void f;
int main()
{
cout << (int &)f << endl; //cout 'f' as it was integer
}
struct {
int a;
double b;
} f{};
You can now actually do something similar with arrays:
#include <iostream>
#include <cstring>
using namespace std;
struct Foo;
extern int arr[];
int main()
{
cout << arr[2] << endl;
}
int arr[4]{};
Life example.
If I were to do this
class Gone
{
public:
static const int a = 3;
}
it works but if do
class Gone
{
public:
static int a = 3;
}
it gives a compile error. Now I know why the second one doesn't work, I just don't know why the first one does.
Thanks in advance.
This trick works only for constant compile-time expressions. Consider the following simple example:
#include <iostream>
class Foo {
public:
static const int bar = 0;
};
int main()
{
std::cout << Foo::bar << endl;
}
It works just fine, because compiler knows that Foo::bar is 0 and never changes. Thus, it optimizes the whole thing away.
However, the whole thing breaks once you take the address of that variable like this:
int main()
{
std::cout << Foo::bar << " (" << &Foo::bar << ")" << std::endl;
}
Linker sends you to fix the program because compile-time constants don't have addresses.
Now, the second case in your example doesn't work simply because a non-constant variable cannot be a constant compile-time expression. Thus, you have to define it somewhere and cannot assign any values in initialization.
C++11, by the way, has constexpr. You can check Generalized constant expressions wiki (or C++11 standard :-)) for more info.
Also, be careful - with some toolchains you will never be able to link program as listed in your first example when optimizations are turned off, even if you never take an address of those variables. I think there is a BOOST_STATIC_CONSTANT macro in Boost to work around this problem (not sure if it works though because I reckon seeing linkage failures with some old gcc even with that macro).
The static const int declaration is legal because you're declaring a constant, not a variable. a doesn't exist as a variable - the compiler is free to optimize it out, replacing it with the declared value 3 anywhere a reference to Gone::a appears. C++ allows the static initialization in this restricted case where it's an integer constant.
You can find more details, including an ISO C++ standard citation here.
Initialization of variables has to be done at the point of definition, not the point of declaration in the general case. Inside the class brackets you only have a declaration and you need to provide a definition in a single translation unit*:
// can be in multiple translation units (i.e. a header included in different .cpp's)
struct test {
static int x; // declaration
static double d; // declaration
};
// in a single translation unit in your program (i.e. a single .cpp file)
int test::x = 5; // definition, can have initialization
double test::d = 5.0; // definition
That being said, there is an exception for static integral constants (and only integral constants) where you can provide the value of the constant in the declaration. The reason for the exception is that it can be used as a compile-time constant (i.e. to define the size of an array), and that is only possible if the compiler sees the value of the constant in all translation units where it is needed.
struct test {
static const int x = 5; // declaration with initialization
};
const int test::x; // definition, cannot have initialization
Going back to the original question:
Why is it not allowed for non-const integers?
because initialization happens in the definition and not declaration.
Why is it allowed for integral constants?
so that it can be used as a compile-time constant in all translation units
* The actual rules require the definition whenever the member attribute is used in the program. Now the definition of used is a bit tricky in C++03 as it might not be all that intuitive, for example the use of that constant as an rvalue does not constitute use according to the standard. In C++11 the term used has been replaced with odr-used in an attempt to avoid confusion.
A static const is defined in the class definition since everybody that uses the code need to know the value at compile time, not link time. An ordinary static is actually only declared in the class definition, but defined once, in one translation unit.
I seem to recall that originally (ARM) it was not allowed, and we used to use enum to define constants in class declarations.
The const case was explicitly introduced so as to support availability of the value in headers for use in constant expressions, such as array sizes.
I think (and please comment if I have this wrong) that strictly you still need to define the value:
const int Gone::a;
to comply with the One Definition Rule. However, in practice, you might find that the compiler optimises away the need for an address for Gone::a and you get away without it.
If you take:
const int* b = &Gone::a;
then you might find you do need the definition.
See the standard, $9.4.2:
ISO 1998:
"4 If a static data member is of const integral or const enumeration
type, its declaration in the class definition can specify a
constantinitializer which shall be an integral constant expression
(5.19). In that case, the member can appear in integral constant
expressions within its scope. The member shall still be defined in a
namespace scope if it is used in the program and the namespace scope
definition shall not contain an initializer."
Draft for c++11:
"3 If a static data member is of const effective literal type, its
declaration in the class definition can specify a constant-initializer
brace-or-equal-initializer with an initializer-clause that is an
integral constant expression. A static data member of effective
literal type can be declared in the class definition with the
constexpr specifier; if so, its declaration shall specify a
constant-initializer brace-or-equal-initializer with an
initializerclause that is an integral constant expression. In both
these cases, the member may appear in integral constant expressions.
The member shall still be defined in a namespace scope if it is used
in the program and the namespace scope definition shall not contain an
initializer."
I am not sure entirely what this covers, but I think it means that we can now use the same idiom for floating point and possibly string literals.
I'm having trouble deciding whether not this code should compile or if just both compilers I tried have a bug (GCC 4.2 and Sun Studio 12). In general, if you have a static class member you declare in a header file you are required to define it in some source file. However, an exception is made in the standard for static const integrals. For example, this is allowed:
#include <iostream>
struct A {
static const int x = 42;
};
With no need to add a definition of x outside the class body somewhere. I'm trying to do the same thing, but I also take the address of x and pass it to a template. This results in a linker error complaining about a lack of definition. The below example doesn't link (missing a definition for A::x) even when it's all in the same source file:
#include <iostream>
template<const int* y>
struct B {
static void foo() { std::cout << "y: " << y << std::endl; }
};
struct A {
static const int x = 42;
typedef B<&x> fooness;
};
int main()
{
std::cout << A::x << std::endl;
A::fooness::foo();
}
Which is bizarre since it works as long as I don't pass the address to a template. Is this a bug or somehow technically standards compliant?
Edit: I should point out that &A::x is not a runtime value. Memory is set aside for statically allocated variables at compile time.
To be a well formed program you stil have to have the defintion of the static variable (without an initializer in this case) if it actually gets used, and taking the address counts as a use:
C++2003 Standard: 9.4.2 Static data members Paragraph 4 (bold added)
If a static data member is of const
integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19).
In that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
namespace scope if it is used in the
program and the namespace scope
definition shall not contain an
initializer
You are trying to pass a runtime value to a template, that's not possible. The only allowed template parameters are types (class/typename) or integral constant values (int/bool/etc).
Interesting, it compiled fine for me on VS 2008. I kind of assumed that the error came from the typedef because at compile time when it tries to compile 'B' with &x as the template type it doesn't then know where the address of x will be. Still... it compiles and gives a reasonable output.
I could see how one might expect this to compile anyway.
The address of a static const isn't really a runtime value and can be fully resolved at link time.