class Test
{
enum{};
...
};
Is this empty enum definition portable? Compiles in gcc and and msvc.
such an enum is specifically listed in clause 7 paragraph 3 of the C++ standard as
ill-formed. gcc does not accept it. there was a bug fix for this in gcc:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29018
According to the following snippet from the c++ standard we can deduce that it's indeed a valid statement:
7.2/1 Enumeration declarations (C++03)...
enum-specifier:
enum identifieropt { enumerator-listopt }
Note that both the identifier and the enumerator-list are optional, and therefor a statement as enum {} is valid (if you ask the standard).
But doesn't the standard also say that empty declarations are ill-formed?
Yes, and there is even an example of enum { }; in the below snippet from the standard.
7/3 Specifiers (C++03)
In these cases and whenever a class-specifier or enum-specifier is
present in the decl-specifier-seq, the identifiers in these specifiers
are among the names being declared by the declaration (as class-
names, enum-names, or enumerators, depending on the syntax).
In such cases, and except for the declaration of an unnamed bit-field
(9.6), the decl-specifier-seq shall introduce one or more names into
the program, or shall redeclare a name introduced by a previous
declaration.
*Example [
enum { }; // ill-formed
typedef class { }; // ill-formed
*end example]
Conclusion
The statement seems to be ill-formed after a careful look at the standard, though compilers are written by humans - and humans tend to make mistakes and sometimes overlook things.
TL;DR You should not use an empty declaration such as enum { };, even though it compiles
Related
Recent versions of clang (since clang-11) issue a warning when compiled with -pedantic for the following segment of code:
namespace foo {
template <int A>
struct Bar {
~Bar();
};
} // namespace foo
template <int A>
foo::Bar<A>::~Bar(){}
With the generated warning (and error with -Werror) being:
<source>:10:12: error: ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~' [-Werror,-Wdtor-name]
foo::Bar<A>::~Bar(){}
~~~~~~~~~~~^~
::Bar
Live Example
clang is the first compiler I've seen to issue this diagnostic, and as far as I'm aware, what was written above is completely valid C++. Two different ways that seem to suppress this is to either define within the same namespace or explicitly qualify the destructor name -- such as:
...
template <int A>
foo::Bar<A>::~Bar<A>(){}
Is Clang's diagnostic here correct? It's my understanding that the type's name would absolutely be in the correct name-scope as foo::Bar<A>::~ -- and that the qualification of ~Bar<A>() should be unnecessary.
From what I have learned since posting the question, this warning is, strictly-speaking, correct -- though it most likely a defect in the wording of the standard.
According to Richard Smith in LLVM Bug 46979:
The diagnostic is correct, per the standard rules as written; strictly-speaking, the C++ rules require this destructor to be written as
template<typename T>
A::B<T>::~B<T>()
Per C++ [basic.lookup.qual]p6:
In a qualified-id of the form:
nested-name-specifier[opt] type-name :: ~ type-name
the second type-name is looked up in the same scope as the first.
This means that the second B is looked up in class A, which finds only the class template B and not the injected-class-name.
This is not an especially useful rule, and likely isn't the intended rule, which is why this diagnostic (along with a bunch of similar diagnostics for plausible but formally incorrect destructor names) is disabled by default but included in -pedantic.
Looking further into this, I can find the mentioned passage for [basic.lookup.qual]/6 in the C++20 standard, but it appears drafts for C++23 have changed this -- which points towards this most likely being a defect.
In drafts for [basic.lookup.qual] for C++23, this whole section has been overhauled and is currently, at the time of writing, replaced with [basic.lookup.qual]/4 which states:
If a qualified name Q follows a ~:
(4.1) If Q is a member-qualified name, it undergoes unqualified lookup as well as qualified lookup.
(4.2) Otherwise, its nested-name-specifier N shall nominate a type.
If N has another nested-name-specifier S, Q is looked up as if its lookup context were that nominated by S.
(4.3) Otherwise, if the terminal name of N is a member-qualified name M, Q is looked up as if ~Q appeared in place of M (as above).
(4.4) Otherwise, Q undergoes unqualified lookup.
(4.5) Each lookup for Q considers only types (if Q is not followed by a <) and templates whose specializations are types.
If it finds nothing or is ambiguous, it is discarded.
(4.6) The type-name that is or contains Q shall refer to its (original) lookup context (ignoring cv-qualification) under the interpretation established by at least one (successful) lookup performed.
[Example 4:
struct C {
typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
void f() {
p->C::I::~I(); // I is looked up in the scope of C
q->I1::~I2(); // I2 is found by unqualified lookup
}
struct A {
~A();
};
typedef A AB;
int main() {
AB* p;
p->AB::~AB(); // explicitly calls the destructor for A
}
— end example]
(The full quote has been posted here since this is from a draft, and the wording may be subject to change in the future)
This appears to explicitly correct this issue by ensuring that lookup is performed as one would expect.
So basically the diagnostic is correct under older versions of C++ due to a defect in the wording -- but C++23 appears to change that. I am not sure whether this will be retroactively fixed in older versions as a defect given that it appears no compiler actually follows this pedantic requirement.
The following namespace definition fails to compile when the first declaration is commented out. If the first declaration of foo is uncommented, then it compiles just fine.
namespace Y
{
//void foo();
void ::Y::foo(){}
}
The relevant part in the standard (§8.3¶1) says:
When the declarator-id is qualified, the declaration shall refer to a previously declared member
I understand that this rule prevents the introduction of names into other namespaces. I wonder if that rule could be relaxed to allow for qualified-ids referring to the current namespace.
CWG #482 is relevant:
According to 8.3 [dcl.meaning] paragraph 1, […]
This restriction prohibits examples like the following:
void f();
void ::f(); // error: qualified declarator
namespace N {
void f();
void N::f() { } // error: qualified declarator
}
There doesn't seem to be any good reason for disallowing such
declarations, and a number of implementations accept them in spite of
the Standard's prohibition. Should the Standard be changed to allow
them?
Notes from the April, 2006 meeting:
In discussing issue 548, the CWG agreed that the prohibition of
qualified declarators inside their namespace should be removed.
So your code is valid if the first declaration of foo is present (as of about 2012; GCC has an open bug report). If not, however, your quoted wording still applies and renders the qualified declaration ill-formed. I see no reason to permit that case; it intuitively implies that the name has been declared already, since qualified name lookup must determine what it refers to.
Consider the following program:
extern int x;
auto x = 42;
int main() { }
Clang 3.5 accepts it (live demo), GCC 4.9 and VS2013 do not (live demo for the former). Who is right, and where is the correct behavior specified in the C++ Standard?
There's surprisingly little in the standard about this. About all we hear about redeclaration is:
[C++11: 3.1/1]: A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. [..]
and the only relevant part of auto's semantics:
[C++11: 7.1.6.4/3]: Otherwise, the type of the variable is deduced from its initializer. [..]
(reminding us that the type of x is int).
We know that a variable must be given the same type by all declarations:
[C++11: 3.5/10]: After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic.
and the "after all adjustments of types" ought to take care of any questions regarding auto's participation in all of this; my interpretation, then, is that this is inherently a valid redeclaration (and definition) of the x at global scope with type int, and that Clang is correct. Even if we propose that auto does not count as "adjustment of type", since no diagnostic is required, at worst all listed implementations are compliant in their own way.
I believe GCC and Visual Studio are taking the following as inspiration:
[C++11: 7.1.6.4/5]: A program that uses auto in a context not explicitly allowed in this section is ill-formed.
…but I think that this is short-sighted. It seems unlikely that the standard language is intended to prohibit the usual redeclaration rules, just because they are not repeated or explicitly referenced from within 7.1.6.4.
C++14 adds wording that relates to declarations of functions with deduced types:
[C++14: 7.1.6.4/13]: Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [..]
By symmetry one might suggest that, in your int case, it is intended that GCC and VS be correct in rejecting the program. However, this is a different feature (since deduction cannot be applied to mere declarations) and thus a different scenario.
Either way, improved standard wording would help here. I consider it a [reasonably minor] editorial defect.
Note
I answered a question that was closed a duplicate of this one. I asked for merge and was told instead to provide an answer here. See below for my original answer.
Update clang is correct
I asked this question on twitter and the response I received from Richard Smith was as follows:
Not a defect, it's intentional that this restriction applies only to deduced return types and not to variable types. For variables, it's just a convenience shorthand, but return type deduction affects something more fundamental about functions (and especially function templates).
So the logic is that this is allowed by [dcl.spec.auto] and to restrict this for deduced return types paragraph [dcl.spec.auto]p11 was added to the section. Otherwise there is no restriction and therefore this is not restricted for the variables case.
Original
Currently [dcl.spec.auto] does not seem to cover this case explictly but it does say in [dcl.spec.auto]p5:
A program that uses auto or decltype(auto) in a context not explicitly allowed in this subclause is ill-formed.
and we can see it makes a similar case for functions ill-formed in [dcl.spec.auto]p11:
Redeclarations or specializations of a function or function template
with a declared return type that uses a placeholder type shall also
use that placeholder, not a deduced type. Similarly, redeclarations or
specializations of a function or function template with a declared
return type that does not use a placeholder type shall not use a
placeholder. [ Example:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
....
So although this could use clarification as currently worded it feels like gcc is correct and this is ill-formed.
I'd imagine the restriction in [dcl.spec.auto]p11 exists because otherwise, that would allow:
int f();
auto f(); // What's the return type here?
The thing is, you can have an undeduced type type has the return type of a function. There are no deduction rules based on previous declarations, which is why such mixing is disallowed for functions, even though the following would be perfectly fine:
int f();
auto f() { return 1; }
This problem does not exist for variables:
extern int v;
extern auto v; // ill-formed
Any declaration-only variables has to use a non-placeholder type. What this means is that if you use a placeholder type for the definition of v, it can get deduced without any problems and then of course has to match the non-placeholder type used in the first declaration.
extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.
How is this statement a definition? Isn't it supposed to be a declaration only as it does not allocate any memory until we define an object of the type struct date?
struct Date { int d , m , y ; };
I am readng this book called "The C++ programming language" by Bjarne Stroustrup, in which it has been said (in section 4.9) that this a declaration as well as a definition.
It's not a statement in either language. C99 defines statements in 6.8, and C++11 defines statements in 6.
In C, it is not a definition, it's a declaration only: 6.7/5 of C99 says:
A definition of an identifier is a declaration for that identifier that:
—for an object, causes storage to be reserved for that object;
—for a function, includes the function body;
—for an enumeration constant or typedef name, is the (only) declaration of the identifier.
Since this is none of those three things, it's not a definition of an identifier. In the C99 grammar, it's a struct-or-union-specifier (followed by a semi-colon), which in turn is a type-specifier (followed by a semi-colon), which is one of the permitted forms of a declaration (6.7/1).
In C++, it is a class-specifier or class definition: 9/2 of C++11 says
A class-specifier is commonly referred to as a class definition.
In both C and C++ it's common to say that "every definition is a declaration", so that's probably why Stroustrup say's it's a declaration as well as a definition.
In C this is strictly true, because of the definition of "definition" above. In C++ I think it's not actually true in the grammar that a class-specifier is a declaration, but a class definition introduces a complete type, while a class declaration introduces an incomplete type. There's nothing you can do with an incomplete type that you can't also do with the complete type, so the class definition is "as good as" a class declaration like struct Date;, and better.
struct Date; // forward declaration
struct Date{ int d, m, y; }; // class definition (struct is a class-key)
Also see ISO 14882:98 9.1-1 and -2 class-definition
Also relevant ISO 14882:98 3.2 One-definition-rule
This is the declaration of a new type struct Date in C and Date in C++. A declaration is not a statement. And no memory is reserved for the declaration of a new type.
It declares the type Date. It defines the Dates members, and therefore the size of the objects it will create.
It has no methods declared, so doesn't need to define anything else for the class to be complete.
Also, if you don't declare or define a constructor, destructor, assignment operator, etc, C++ will try to automatically synthesise them for you. So this minimal definition of Date includes a default constructor, assignment operator, and destructor.
Statement from ISO standard $3.1 : 1st point
n3242 Says:
A declaration (Clause 7) may
introduce one or more names into a
translation unit or redeclare names
introduced by previous declarations.
If so, the declaration specifies the
interpretation and attributes of these
names. A declaration may also have
effects including:
— a static assertion (Clause 7),
— controlling template instantiation (14.7.2),
— use of attributes (Clause 7), and
— nothing (in the case of an empty-declaration).
ISO 2003 DOC says:
A declaration (clause 7) introduces
names into a translation unit or
redeclares names introduced by
previous declarations. A declaration
specifies the interpretation and
attributes of these names.
can any one explain what is the difference .
They said "A declaration may also have effects including: " ...CAn any one explain what are these effects in terms of Programming
Please explain these effects in Programming way(with an example program)?
I believe it just that some new features have changed the way a declaration works - in the small details.
For example, this does't just introduce some names, but also affects compilation of the code.
struct A
{
int x;
};
struct B
{
A a;
static_assert(sizeof(a) > 10, "Wrong member size");
};
We also have the empty declaration (which I belive can only be used inside a class):
struct C
{
void f()
{ }; // Semicolon here is allowed, but is an empty declaration
};
The empty declaration is a declaration that does not introduce a name (because it is empty).
Figured out the "affects template instantiation" as well, I think:
template<class T>
class X
{
// some members
};
extern template class X<int>;
extern template class X<char>;
Tells the compiler that X<int> and X<char> will be instantiated somewhere else and does not have to be generated here.