This question already has answers here:
Mixing class and struct
(6 answers)
Closed 9 years ago.
Suppose there is a code like this:
template <typename T>
CLASS_KEY1 X{};
PREFIX template CLASS_KEY2 X<int>;
where CLASS_KEY1, CLASS_KEY2 and PREFIX are macros. CLASS_KEY1 and CLASS_KEY2 may be expanded to class, struct or union keywords. PREFIX may be expanded to empty set of characters or extern keyword.
Here is the table which shows when such code compiles (Yes - compiles, No - does not compile) for all combinations of macros values (compiler gcc-4.8.1, option -std=c++11):
PREFIX extern extern extern
CLASS_KEY1\CLASS_KEY2 class struct union class struct union
class Yes Yes? No Yes Yes? No
struct Yes? Yes No Yes? Yes No
union No No Yes No No Yes
Is it a bug in gcc or standard requirement (strange cases are labeled with question marks)? What about other compilers?
Section 7.1.6.3 (Elaborated type specifiers) of the C++11 standard says :
The class-key or enum keyword present in the elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers. This rule also applies to the form of elaborated-type-specifier that declares a class-name or friend class since it can be construed as referring to the definition of the class. Thus, in any elaborated-type-specifier, the enum keyword shall be used to refer to an enumeration (7.2), the union class-key shall be used to refer to a union (Clause 9), and either the class or struct class-key shall be used to refer to a class (Clause 9) declared using the class or struct class-key.
So, the behavior you're seeing is allowed.
Related
I can create the following:
using Foo = struct { /*Implementation*/ };
template<class>
using Bar = Foo;
However the following is not allowed:
template<class>
using Bar = struct { /*Implementation*/ };
The error from Clang is more helpful than GCC, and states:
error: '(anonymous struct at file:line:column)' cannot be defined
in a type alias template
Any reasons why the second code example is not allowed?
Note:
Please state any examples for which the second code example (if allowed) may cause problems with the language.
Any citation from the standard is also helpful.
Defining a class or an enumeration in an alias-declaration that is part of a template alias is forbidden by [dcl.typedef]/2:
A typedef-name
can also be introduced by an
alias-declaration.
...
The
defining-type-specifier-seq
of the
defining-type-id
shall not define a class or enumeration if the
alias-declaration
is the declaration of
a
template-declaration.
The latter was introduced as CWG issue 1159 was accepted, as part of FCD N3092.
The comments and proposed resolution of the associated N3092 comment US 74 does provide some rationale as to why this restriction was introduced [emphasis mine]:
Comment (ID) US 74
Comment
An alias-declaration allows a class or enumeration type to be defined
in its type-id (7.1.6p3). However, it's not clear that this is
desirable when the alias-declaration is part of a template alias:
template<typename T> using A =
struct { void f(T) { } };
Proposed resolution
Either prohibit the definition of classes and enumerations in
template aliases, or prohibit the use of template parameters in such
definitions, or add an example illustrating this usage.
Owner & issue
CWG 1159
Disposition
ACCEPTED
Definition of a class or enumeration is now prohibited in a template alias.
It would seem as if no one protested (convincingly enough) to prohibiting the definition of classes and enumerations in template aliases, implying that it's likely that no one was able to give a convincing example illustrating where this would be useful.
The following source code is brought from:
Understanding partial specialization of inherited nested class templates
#include <type_traits>
struct Base
{
template<class U, class _ = void> struct Inner: std::true_type {};
template<class _> struct Inner<char, _>: std::false_type {};
};
struct Derived : Base
{
};
template<class _> struct Derived::Inner<int, _>: std::false_type {};
I had an issue about specializing inherited class, so I googled, and find out the question above. The source code in the question above compiled w/o any problem in gcc/clang, but msvc refuses to compile it, issuing C2427 (see https://msdn.microsoft.com/en-us/library/10het5hx.aspx).
Situation of the above (specialize the nested template class of a non-template class) is quite different from the situation described in https://msdn.microsoft.com/en-us/library/10het5hx.aspx (define the nested non-template class of a template class), I think.
Which one of msvc vs. gcc/clang is wrong? Or just the standard is so unclear to specify this behavior?
I hope msvc is wrong...
Clang and GCC are wrong, and MSVC and EDG are right to reject that partial specialization definition.
A partial specialization is itself a template, and a class template definition is syntactically constructed in terms of a class definition (in grammar terms, a class-specifier). Within such a definition, Derived::Inner<int, _> is a class-head-name, with Derived:: being a nested-name-specifier.
[9p11] in the Standard says:
If a class-head-name contains a nested-name-specifier, the
class-specifier shall refer to a class that was previously declared
directly in the class or namespace to which the nested-name-specifier
refers, or in an element of the inline namespace set (7.3.1) of that
namespace (i.e., not merely inherited or introduced by a
using-declaration), and the class-specifier shall appear in a
namespace enclosing the previous declaration. [...]
So, you have to use Base::Inner<int, _>.
As noted in the comments, the quote above applies to class template explicit specialization definitions as well (their grammar production also ends up using class-head-name).
The following doesn't apply directly to your example, but I found it worth mentioning.
Note that the quote above refers to class template (or explicit specialization) definitions, not declarations such as
template<class _> struct Derived::Inner<int, _>;
Syntactically, struct Derived::Inner<int, _> in there is an elaborated-type-specifier, to which the paragraph above doesn't apply. So, the Standard wording technically allows such declarations.
This doesn't seem to be an oversight: the wording above was introduced by the resolution of DR284, which includes the comment:
The sentiment was that this should be required on class definitions,
but not on elaborated type specifiers in general (which are
references, not declarations). [...]
The proposed resolution included elaborated-type-specifiers, but those were removed from the final wording.
However, neither MSVC nor EDG accept such declarations (and frankly I'd find it confusing if they did). The comment in the DR seems to indicate that the intent was to allow only elaborated-type-specifiers that are not also declarations, but it looks like this wasn't reflected in the wording (a Standard bug, I think).
This question already has an answer here:
Why are redundant scope qualifications supported by the compiler, and is it legal?
(1 answer)
Closed 6 years ago.
I noticed by accident that this code compiles and works correctly:
struct M { int some_int; };
static_assert(std::is_same<
decltype(M::M::M::M::some_int) /* <- this */,
int>::value, "Types must be int");
Why is this correct (decltype(M::M::M::M::some_int) <=> decltype(M::some_int))?
What other constructs one can use this pattern with class::class::...::member?
Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23824.1 for x86
This is valid in all contexts, not just decltype. A class contains its own name as the injected class name. So within a class A::B::M, the name M is injected to refer to the class A::B::M. This means that you can then use M::M::M::some_member to refer to members of that class, if you really want to.
[Live example]
Note that when referring just to the class name itself (e.g. M::M::M), the situation is slightly different. If such a reference occurs in a place where a reference to a function could also potentially be correct, the syntax is taken to refer to the constructor instead. However, in type-only contexts, even such reference is valid. Example:
M::M::M m; // illegal, M::M interpreted as reference to constructor
struct D : public M::M::M // legal, a function could not be references here, so M::M::M means M
{};
This works because of the injected-class-name:
(N3337) [class]/2:A class-name is inserted into the scope in which it is declared immediately after the class-name is seen.
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]
So you can arbitrarily nest these, and they'll work with derived types as well:
struct A { using type = int; };
struct B : public A {};
using foo = B::B::B::A::A::A::type;
Note that in the case of A[::A]*::A, the injected-class-name can be considered to name the constructor instead:
[class.qual]/2: In a lookup in which the constructor is an acceptable lookup result and the nested-name-specifier nominates
a class C:
— if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name
of C (Clause 9), or
— [...]
the name is instead considered to name the constructor of class C.
From a practical point of view, I understand that both typedef and test are somewhat "superfluous" and need to be removed if we want the following code to compile:
template< typename type_t >
typedef struct tagTest
{
int a;
} test;
However, I thought that the set of typedef declarations was a subset of the set of declarations. They just happened to have that specific decl-specifier. That was my rationalization for
typedef struct tagTest
{
int a;
} test;
introducing the identifier test and declaring the structure tagTest. If that interpretation is correct, then the following paragraph from the standard should allow template typedef's (although not with the meaning given by the keyword using).
The declaration in a template-declaration shall
—
(1.1)
declare or define a function, a class, or a variable, or
—
(1.2)
define a member function, a member class, a member enumeration, or a static data member of a class
template or of a class nested within a class template, or
—
(1.3)
define a member template of a class or class template, or
—
(1.4)
be an alias-declaration.
I cannot see error in my reasoning, yet the conclusion is illegal.
What are the relevant parts of the standard that solve the above conundrum?
UPDATE
Part of the above reasoning uses the fact that typedef struct declares a structure. The typedef specifier, as far as I understand it, implies that any variables declared are really types. That is, the typedef upgrades test from a mere variable to a type that is equivalent to the declared tagTest. That is why the following code compiles (albeit with a warning).
typedef struct tagTest
{
int a;
};
tagTest t;
One of the answers takes care of the superfluous test. But, it is possible to use typedef without a declarator because "Init-declarator-list is optional when declaring a named class/struct/union or a named enumeration"
Template typedefs weren't allowed pre-C++11 and with C++11 template aliases were introduced to address those issues. Cfr. C++ template typedefs and wikipedia.
Since, as you noted, the standard doesn't allow typedef to be in there, the code is invalid
alias-declaration:
using identifier attribute-specifier-seqopt= type-id ;
typedef declarations are not alias declarations.
Furthermore you can't have a declarator if you're declaring a class template, it is explicitly forbidden by the standard
[temp]/p3
In a template-declaration, explicit specialization, or explicit instantiation the init-declarator-list in the declaration
shall contain at most one declarator. When such a declaration is used to declare a class template,
no declarator is permitted.
so not even the following will compile
template< typename type_t >
struct tagTest
{
int a;
} test;
Edit:
It is nowhere specified that
typedef struct S { };
should be an error, thus both gcc and clang accept it with a warning. I assume Clang counts on [temp]/3 to issue an error in case typedef was being used with a template while gcc rejects this code immediately
template<typename T>
typedef struct S { };
cfr. clang bug 22249
Independently from what the typedef defines, that is a typedef declaration, which is not listed in those cases:
[member] function
[member] class
variable
member enumeration
static data member of a class template/of a class nested within a class template
member template of a class or class template
alias declaration
And just to be clear typedef declarations are not alias declarations. Alias declaration, as specified by the grammar at §7 of the standard are:
alias-declaration:
using identifier attribute-specifier-seqopt= type-id ;
Not to mention that if this was possible, then template using declaration would not be nearly as "cool" as they are today, and there would be little to no sense to have both.
C does not support templates and the
typedef struct tagX {
} X;
syntax in C++ is vestigial C, there to allow continued support for C headers etc, not for use in actual C++.
The C++ syntax for the above is
struct X {};
(YMMV on brace placement)
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.