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.
Related
I have those classes:
#include <type_traits>
template <typename T>
class A {
public:
static_assert(std::is_default_constructible_v<T>);
};
struct B {
struct C {
int i = 0;
};
A<C> a_m;
};
int main() {
A<B::C> a;
}
When compiling, a_m is not default constructible but a is.
When changing C to:
struct C {
int i;
};
everything is fine.
Tested with Clang 9.0.0.
This is disallowed both by the text of the standard and by several major implementations as noted in the comments, but for completely unrelated reasons.
First, the "by the book" reason: the point of instantiation of A<C> is, according to the standard, immediately before the definition of B, and the point of instantiation of std::is_default_constructible<C> is immediately before that:
For a class template specialization, [...] if the specialization is
implicitly instantiated because it is referenced from within another
template specialization, if the context from which the specialization
is referenced depends on a template parameter, and if the
specialization is not instantiated previous to the instantiation of
the enclosing template, the point of instantiation is immediately
before the point of instantiation of the enclosing template.
Otherwise, the point of instantiation for such a specialization
immediately precedes the namespace scope declaration or definition
that refers to the specialization.
Since C is clearly incomplete at that point, the behavior of instantiating std::is_default_constructible<C> is undefined. However, see core issue 287, which would change this rule.
In reality, this has to do with the NSDMI.
NSDMIs are weird because they get delayed parsing - or in standard parlance they are a "complete-class context".
Thus, that = 0 could in principle refer to things in B not yet declared, so the implementation can't really try to parse it until it has finished with B.
Completing a class necessitates the implicit declaration of special member functions, in particular the default constructor, as C doesn't have a constructor declared.
Parts of that declaration (constexpr-ness, noexcept-ness) depend on the properties of the NSDMI.
Thus, if the compiler can't parse the NSDMI, it can't complete the class.
As a result, at the point when it instantiates A<C>, it thinks that C is incomplete.
This whole area dealing with delayed-parsed regions is woefully underspecified, with accompanying implementation divergence. It may take a while before it gets cleaned up.
Undefined behavior it is:
If an instantiation of a template above depends, directly or
indirectly, on an incomplete type, and that instantiation could yield
a different result if that type were hypothetically completed, the
behavior is undefined.
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...
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.
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
Consider this code:
template <int N>
struct X
{
friend void f(X *) {}
};
int main()
{
f((X<0> *)0); // Error?
}
compilers seem to heavily disagree. (MSVC08/10 says no, GCC<4.5 says yes, but 4.5 says no, sun 5.1 says yes, intel 11.1 says yes too but comeau says no (both are EDG)).
According to "C++ Templates - The complete guide":
... it is assumed that a call
involving a lookup for friends in
associated classes actually causes the
class to be instantiated ... Although
this was clearly intended by those who
wrote the C++ standard, it is not
clearly spelled out in the standard.
I couldn't find the relevant section in the standard. Any reference?
Consider this variation:
template <int N>
struct X
{
template <int M>
friend void f(X<M> *) {}
};
template <>
struct X<0>
{
};
int main()
{
X<1>();
f((X<0> *)0); // Error?
}
The key issue here is wether the viable function injected by X<1> should be visible during ADL for X<0>? Are they associated? All compilers mentioned above accept this code, except for Comeau which only accepts it in relaxed mode. Not sure what the standard has to say about this either.
What's your take on that?
The Standard says at 14.7.1/4
A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type affects the semantics of the program; in particular, if an expression whose type is a class template specialization is involved in overload resolution, pointer conversion, pointer to member conversion, the class template specialization is implicitly instantiated (3.2);
Note that Vandervoorde made an issue report here, and the committee found
The standard already specifies that this creates a point of instantiation.
For your second case - you need to consider the associated classes and namespaces of the argument f(X<0>*). These are, since this is a pointer to a class template specialization (note that "template-id" below is not quite correct - C++0x corrected that to use the correct term) and also a pointer to a class (this confusing split was also corrected in C++0x - it lists these two cases in one bullet point).
If T is a template-id, its associated namespaces and classes are the namespace in which the template is
defined; [... lots of noise ...]
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
So to summary, we have as associated classes are X<0> and the associated namespaces are the global namespace. Now the friend functions that are visible are
Any namespace-scope friend functions declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup
There is no friend function declared in X<0> so the friend function declaration is not visible when looking into the global namespace. Note that X<0> is an entirely different class-type than X<1>. The implicit instantiation of X<1> you do there has no effect on this call - it just adds a non-visible name into the global namespace that refers to a friend function of class X<1>.