Inconsistent specialization of tuple_size - c++

When explicitly specializing both tuple_size and tuple_element for some template, I realized that §14.5.1/4, which reads
In a redeclaration, partial specialization, explicit specialization or
explicit instantiation of a class template, the class-key shall agree
in kind with the original class template declaration (7.1.6.3).
is seemingly violated by the standard itself:
Am I missing something here (e.g. the standard specifies that class-keys are "exposition only" and shall agree within the actual implementation)?

Bo Persson's now-deleted answer got it right. The paragraph ([temp.class]/p4) you cite refers to §7.1.6.3 [dcl.type.elab] for the definition of "agree in kind", which in turn says (p3, emphasis mine)
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.
The code is valid, if inconsistent.

This looks like an editorial defect to me. Try opening an issue here:
https://github.com/cplusplus/draft/issues
If you're adventurous, try resolving it with a pull request. :-)

Related

Is it legal to use a different class-key in a declaration and the definition of a class-name?

Does the standard (as of C++20) explicitly or implicitly allow using a different class-key when (forward-)declaring a class-name than when defining it?
For the purpose of this question class-key shall be limited to class or struct, excluding union.
In other words, is this legal:
struct C;
class C{};
class S;
struct S{};
An answer should explicitly refer to the C++20 standard (or a suitable draft).
I couldn't find anything in the entire [class] section.
The only indication I could find is in [decl.type.elab.6] which first states:
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.
But to me it is unclear what kind means in this context. I suppose enum and struct are different kinds but are struct and class different kinds? I don't know. Anyway after some unrelated points about friends it goes on to say:
Thus, in any elaborated-type-specifier, the enum keyword shall be used
to refer to an enumeration ([dcl.enum]), the union class-key shall be
used to refer to a union ([class.union]), and either the class or
struct class-key shall be used to refer to a non-union class
([class.pre]).
(emph added)
This sort of sounds as if struct and class were interchangeable, but I'm not sure about it.
According to code explorer it appears to be generally accepted by compilers.
Non-Duplicates
I do not believe Mixing class and struct answers the question. Due to its age, it talks about a different version of the standard. Also, the one answer that actually quotes the standard does not convince: The quoted paragraph has a lot of ambiguity and does not address the question asked here.
Forward declaration as struct vs class has no answers actually bothering to prove their assertions by citation.
Changing struct to class (and other type changes) and ABI/code generation Asks about two definitions with different class-keys. Its answer does not apply here.
The standard explains this in 9.2.8.3.3 and clarifies it in the example, as Peter already pointed out in a comment to your question:
[...] either the class or struct class-key shall be used to refer to a non-union class
[...]
struct S { } s;
class S* p = &s; // OK
The compilers agree, as can be seen by the warning clang generates:
warning: 'C' defined as a struct here but previously declared as a class; this is valid, but may result in linker errors under the Microsoft C++ ABI [-Wmismatched-tags]
GCC has a faq entry:
Because it is not a bug, hence the warning is just noise. This is a
dumb warning that only exists because the MS compiler has a bug that
treats struct and class differently in mangled names. GCC (and Clang)
correctly implement the C++ standard which says it doesn't matter.

When/why/how do unqualified names look in dependent base?

For up to C++17 I find this wording in [temp.dep]p3
In the definition of a class or class template, the scope of a dependent base class (17.7.2.1) is not examined
during unqualified name lookup either at the point of definition of the class template or member or during
an instantiation of the class template or member.
But looking at the newest draft (on eel.is), it appears this changed. This text does not appear at the place anymore, and I don't know whether this rule is still in place, or in weaker form (maybe they are looked up if they are dependent, but illformed if a base-class member is found?) or not at all!
Nothing has changed. The relevant rule is now [class.member.lookup]/4:
Calculate the lookup set for N in each direct non-dependent ([temp.dep.type]) base class […]
so that there need not be a special override for the name-lookup rules in [temp].

Template alias scope

As per http://en.cppreference.com/w/cpp/language/type_alias, aliases are block-level declarations. It doesn't say anything special about template aliases, so it should be read that template aliases are block-level declarations as well.
However, it is impossible to use template aliases at block level. The errors are different depending on the compiler - while g++ gives a meaningful message, saying that templates are not allowed at block scope, clang is completely cryptic. (example: http://coliru.stacked-crooked.com/a/0f0862dad6f3da61).
Questions I have so far:
Does cppreference fail to specify that template aliases can not be used at block scope? (Or do I need to take a reading course?)
Are the compilers correct in denying template aliases on block level (the feature I find very interesting for my particular coding habits)
If the answer to the second is Yes, what might be the rationale for this? Why would compiler deny me this pure syntax sugar?
An alias template is [temp.alias]
A template-declaration in which the declaration is an alias-declaration (Clause 7) declares the identifier to
be a alias template. An alias template is a name for a family of types. The name of the alias template is a
template-name.
And if we look at 14.2 [temp] we have
A template-declaration can appear only as a namespace scope or class scope declaration. In a function
template declaration, the last component of the declarator-id shall not be a template-id.
So yes cppreference is off saying that it can be declared at block scope and your compilers are correct. If you do click on the link of block declarations It will bring you to a list of declarations and in that it has Template declaration and in there it has
declaration of a class (including struct and union), a member class or member enumeration type, a function or member function, a static data member at namespace scope, a variable or static data member at class scope, (since C++14) or an alias template (since C++11) It may also define a template specialization.
As for why the standard says that templates can only be declared in namespace scope or class scope I like James Kanze answer
The problem is probably linked to the historical way templates were implemented: early implementation techniques (and some still used today) require all symbols in a template to have external linkage. (Instantiation is done by generating the equivalent code in a separate file.) And names defined inside a function never have linkage, and cannot be referred to outside of the scope in which they were defined.
The compilers are behaving correctly.
Section 14 of the C++14 standard:
A template-declaration can appear only as a namespace scope or class
scope declaration.

Legality of empty, unnamed enumeration specifier

In the example in §7.5 (C++14) one finds:
enum {}; // ill-formed
But, technically speaking, I think the code is valid.enum {} is an enum-specifier, and so, it's a type-specifier, which is a decl-specifier, and thus, it is a simple-declaration with an omitted init-declarator-list. And this is accepted by §7.5. Note that the identifier is optional for an unscoped enumeration. Also, clang compiles this with a warning.
Edit
In relation to the answers mentioning that the *decl-specifier-seq* shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration, I show below a typedef declaration that compiles, but whose decl-specifier-seq doesn't introduce any name in the declaration:
typedef class {} A;
In the C++14 FD, right above your example, it is explained why the declaration is ill-formed:
In a simple-declaration, the optional init-declarator-list can be
omitted only when declaring a class (Clause 9) or enumeration (7.2)
[…]
In such cases, the decl-specifier-seq shall introduce one or more
names into the program, or shall redeclare a name introduced by a
previous declaration.
Hence
enum {} e;
enum {a};
Are valid - our quote does not apply to the first declaration as it includes an init-declarator, and the second one introduces the name a as an enumerator. Note that GCC won't compile the first declaration, which is presumably a bug.
You also mentioned the quote
If the enumerator-list is empty, the underlying type is as if the
enumeration had a single enumerator with value 0.
This makes a statement about the underlying type, not the enumeration itself, and is thus irrelevant for this matter.
Why does typedef class {} A; compile?
A is an init-declarator. Yes, it is - [dcl.dcl]/9:
If the decl-specifier-seq contains the typedef specifier, the
declaration is called a typedef declaration and the name of each
init-declarator is declared to be a typedef-name
I.e. the names declared as typedef-names are the init-declarators, by this definition. Hence there is an init-declarator-list, and our above quote isn't applicable.
If you look through the standard, you'll find quite a few things that would be accepted at a purely syntactical level, but are prohibited by the text. This is just one of many instances of that basic idea.
Many of these situations are pretty obvious. Let's consider a really trivial one: a floating point number. Using a syntactical notation similar to that of the standard we could get something like:
"-"opt digits opt "."opt digits opt ("e" "-"opt digits)opt
Everything there is "optional". That doesn't, however, mean that nothingness should be taken as a number. Nor does it mean that (for example) .e is a valid floating point number. It does mean that almost any individual piece can be omitted if some of the others are present. You don't need digits both before and after the decimal point, so each is optional in itself--but one or the other has to be present, so 1. and .1 are both valid, but just . isn't. Likewise, the . is optional as well--something like 1e50 is a perfectly valid floating point literal, even though it doesn't contain a . anywhere.
Those limitations are expressed in the text associated with the syntactic notation itself. What's allowed (or not) has to based on everything taken together, not just on one piece in isolation.
There is clear written in the Standard that (7 Declarations)
...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.
This declaration
enum {}; // ill-formed
does not satisfy the requirement. It introduces neither name into the program.
As for typedef(s) then
7.1.3 The typedef specifier 1 Declarations containing the decl-specifier typedef declare identifiers that can be used later
for naming 94)

Ambiguity Resolution

void S(){}
struct S{};
int main(){
S();
}
In the code above, the expression 'S()' in main is treated as a function call expression rather than an attempt to create a temporary of type 'S'.
Which portion of the C++ Standard talks about the resolution of such an expression in favour of a function declaration? For some reason I am unable to locate it.
Section 3.3.7/2
A class name (9.1) or enumeration name (7.2) can be hidden by the name of an object, function, or enumerator declared in the same scope. If a class or enumeration name and an object, function, or enumerator are
declared in the same scope (in any order) with the same name, the class or enumeration name is hidden
wherever the object, function, or enumerator name is visible.
Then you need to use elaborated type specifier in such cases
3.4.4/1 Elaborated type specifiers
An elaborated-type-specifier may be used to refer to a previously declared class-name or enum-name even
though the name has been hidden by a non-type declaration (3.3.7). The class-name or enum-name in the
elaborated-type-specifier may either be a simple identifer or be a qualified-id.
It can be resolved either by using the scope resolution operator(::) or by using virtual keyword(when we are dealing with either multiple or hybrid inheritance.