Consider the following program:
extern class A;
int main() {}
Is this well-formed according to the c++ standard? If it is ill-formed is diagnostics required? I'm getting different results for different compilers:
Clang: No compiler errors (only a warning): http://melpon.org/wandbox/permlink/lhb8XNU01IyVhMnc
GCC: Compiler error: http://melpon.org/wandbox/permlink/mIH9qmNY4noI1sEc
Visual c++: No compiler errors (only a warning): http://webcompiler.cloudapp.net/
The program is ill-formed according to §7.1.1/1:
If a storage-class-specifier appears in a decl-specifier-seq, […]
the init-declarator-list of the declaration shall not be empty
(except for an anonymous union declared in a named namespace or in the
global namespace, which shall be declared static (9.5)).
Related
The following code snippet:
struct a
{
[[nodiscard]] friend int b();
};
Produces this error when compiling on clang++ (trunk 342102) with -std=c++17:
<source>:3:5: error: an attribute list cannot appear here
[[nodiscard]] friend int b();
^~~~~~~~~~~~~
Removing friend or adding a body to b prevents the error.
g++ (trunk) compiles the code just fine.
Live example on godbolt: https://gcc.godbolt.org/z/ttTDuZ
Is this a clang++ bug? Or is there some rule in the Standard that makes this code ill-formed?
If clang++ is correct, what's the proper way of marking a friend member function as [[nodiscard]]?
Per [dcl.attr.grammar]/5
Each attribute-specifier-seq is said to appertain to some entity or statement, identified by the syntactic context where it appears ([stmt.stmt], [dcl.dcl], [dcl.decl]). If an attribute-specifier-seq that appertains to some entity or statement contains an attribute or alignment-specifier that is not allowed to apply to that entity or statement, the program is ill-formed. If an attribute-specifier-seq appertains to a friend declaration, that declaration shall be a definition. No attribute-specifier-seq shall appertain to an explicit instantiation.
emphasis mine
So, clang is right here. If you have an attribute, the function must have a definition if it is a friend function.
This program is in the file called localFunc.c:
#include <stdio.h>
// int f(int); // Global forward declaration.
int main() {
int f(int); // Local forward declaration.
printf("%d\n", f(1));
}
double f(int i) {
return 1.0;
}
Compiling via gcc localFunc.c gives:
localFunc.c:10:8: error: conflicting types for ‘f’
double f(int i) {
^
localFunc.c:6:7: note: previous declaration of ‘f’ was here
int f(int); // Local forward declaration.
But compiled via g++ localFunc.c, there is no error and the result of running the executable is: 4195638.
Commenting out the Local forward declaration, and turning on the Global forward declaration of: int f(int);, both gcc and g++ give errors similar to the above, as expected(by me).
So my question is, why does it appear that g++ is not seeing the conflicting types(ambiguous declaration) on locally declared functions?
don#HAL:~/UNIX/CS213$ gcc --version
gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
I can explain this behavior for C++.
From the C++11 spec, section 3.5 [basic.link], paragraph (7):
When a block scope declaration of an entity with linkage is not found to refer to some other declaration, then that entity is a member of the innermost enclosing namespace. However such a declaration does not introduce the member name in its namespace scope.
So the int f(int) declaration has global linkage, but does not introduce the name into the global namespace. This might explain why GCC does not notice the conflict.
More importantly, paragraph (10) says:
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.
Your program violates the "shall" portion of this paragraph, so its behavior is undefined. That means anything GCC does whatsoever -- such as calling the double foo(int) function and treating its return value as an int -- is permitted by the spec. Moreover, no diagnostic is required.
I do not know what C says about this case; in particular, whether the diagnostic is required.
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...
As far as I can see in the standard, the following code is valid. It compiles in MSVC1025.
const struct omg;
struct omg volatile;
int main()
{
return 0;
}
The qualifiers const and volatile seem purposeless in those declarations. They do not help nor hurt neither the compiler nor the programmer.
The standard does not seem bent on weeding out these "empty ambiguities". In the case of the empty declaration ;, it is explicitly allowed.
Are there other cases of tokens that, after preprocessing, are irrelevant for the meaning of the expression?
Both clang and gcc reject this code using -pedantic-errors. clang provides the following error:
error: 'const' is not permitted on a declaration of a type [-Werror,-Wmissing-declarations]
const struct omg;
^
error: 'volatile' is not permitted on a declaration of a type [-Werror,-Wmissing-declarations]
the draft C++ standard section 7.1.6.1 The cv-qualifiers [dcl.type.cv] says:
[...]If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list of the declaration shall
not be empty.[...]
This code
int clash;
struct Foo {
decltype(clash) clash;
};
compiles silently on clang, but fails to compile on gcc giving the errors
error: declaration of 'int Foo::clash' [-fpermissive]
error: changes meaning of 'clash' from 'int clash' [-fpermissive]
It seems that 2 ingredients are required for the error to arise:
The shadowing must be done by a class member (no problem if it's a function's local scope).
decltype([shadowed name]) must be used in the shadowing scope before the declaration of [shadowing name].
My question is twofold:
Is gcc justified in rejecting this code?
Where does it say so in the standard?
gcc is correct the program is ill-formed, although this particular violation does not require a diagnostic so clang does not have to provide one.
If we look at the C++11 standard(The closest draft would be N3337) section 3.3.7 Class scope it says:
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.
and the next rule says:
If reordering member declarations in a class yields an alternate valid
program under (1) and (2), the program is ill-formed, no diagnostic is
required.
It makes sense we would want to prevent situations where reordering the declarations in a class give a different program. It is curious whether these two rules are redundant or not.
The section also provides the following example:
enum { i = 1 };
class X {
char v[i]; // error: i refers to ::i
// but when reevaluated is X::i
int f() { return sizeof(c); } // OK: X::c
char c;
enum { i = 2 };
};
and if we try this example with gcc (see it live), we get an almost identical error to one your code produces:
error: declaration of 'i' [-fpermissive]
enum { i = 2 };
^
error: changes meaning of 'i' from '<anonymous enum> i' [-fpermissive]
enum { i = 1 };