std::is_same for a not yet defined/declared class - c++

Is thw following code, a gcc bug?
Checking if T type is a not defined yet class Circle, returns false.
#include <iostream>
using namespace std;
// uncomment to work
//struct Circle;
struct T_traits
{
template<typename T>
constexpr static id() { return is_same<T, class Circle>(); }
};
struct Circle{};
int main()
{
cout << T_traits::id<Circle>() << "\r\n";
return 0;
}

return is_same<T, class Circle>();
This will actually declare a local class called Circle when you comment out the global declaration. [basic.lookup.elab]/2:
If the elaborated-type-specifier has no nested-name-specifier, and
unless the elaborated-type-specifier appears in a declaration with
the following form: class-key
attribute-specifier-seqopt identifier ; the
identifier is looked up according to 3.4.1 but ignoring any non-type
names that have been declared.[..]
If the elaborated-type-specifier is introduced by the class-key
and this lookup does not find a previously declared type-name
[..] the elaborated-type-specifier is a declaration that
introduces the class-name as described in 3.3.2.
The lookup is simple unqualified name lookup as defined in §3.4.1. Lookup is done in the definition context of T_traits as we are not dealing with dependent stuff, so the declaration of Circle right before main is never considered.
§3.3.2/7 (alias [basic.scope.pdecl]/7):
The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:
for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the
decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a
class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the
declaration. [ Note: These rules also apply within templates. — end
note ]
However, removing the class keyword won't work either - as mentioned before, the identifier isn't dependent and thus looked up in the definition context. If no declaration is found by this lookup, a diagnostic must be issued by the compiler - even if no specialization is instantiated.

Related

The scope of a declaration in C++ template specialization

For the following code:
namespace A
{
struct B
{
using type = std::tuple<struct C>;
};
}
int main()
{
C* ptr = nullptr;
B::C* ptr2 = nullptr;
A::B::C* ptr3 = nullptr;
A::C* ptr4 = nullptr;
}
I just want to know what is the scope of C. I have tried in gcc 6.5/7.4/8.3/9.1 and clang 6/7/8, they all told me that A::C is correct. But I am not sure whether there are any materials in the C++ standard that describe the scope of C in the above situation.
Could you tell me if you know the materials in the C++ standard that related to this topic? Thanks very much !
This is detailed in the C++ standard at the following sections:
[basic.lookup.elab]
2 If the elaborated-type-specifier is introduced by the class-key
and this lookup does not find a previously declared type-name, or if
the elaborated-type-specifier appears in a declaration with the form:
class-key attribute-specifier-seqopt identifier ;
the elaborated-type-specifier is a declaration that introduces the
class-name as described in [basic.scope.pdecl].
[basic.scope.pdecl] (emphasis mine)
7 The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:
[...]
... if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function
defined in namespace scope, the identifier is declared as a class-name
in the namespace that contains the declaration; otherwise, except as
a friend declaration, the identifier is declared in the smallest
namespace or block scope that contains the declaration.
The argument list of a template falls in the "otherwise" clause. A is the smallest namespace that contains the declaration, so the class type C is declared inside of it. A::C is the correct way to refer to it.

Alternative to forward declaration

I noticed that prepending the class or struct keyword to a type that would otherwise need be forward declared works as if that type was forward declared:
// struct Test; forward declaration commented
void* foo(struct Test* t) // C style function parameter - This works !
{
return t;
}
I wasn't aware of that. I wonder if it's standard C++ or an extension and whether the struct keyword before the parameter works as a forward declaration or another mechanism kicks in.
Furthermore, after such a usage the "next" function can use the type without prepending any keywords :
void* oof(Test* t);
Demo
This is legal, but probably not a good idea.
From [basic.scope.pdecl]/6:
[...] — for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
function defined in namespace scope, the identifier is declared as a class-name in the namespace that
contains the declaration [...]
For example:
namespace mine {
struct T* foo(struct S *);
// ^^^^^^^^^---------------- decl-specifier-seq
// ^^^^^^^^^^--- parameter-declaration-clause
}
This introduces T and S as class-names and foo as a function name into namespace mine.
Note that the behavior is different in C; the struct name is only valid within the scope of the function.
6.2.1 Scopes of identifiers
4 - [...] If the declarator or type specifier that
declares the identifier appears [...] within the list of parameter declarations in
a function definition, the identifier has block scope, which terminates at the end of the
associated block. If the declarator or type specifier that declares the identifier appears
within the list of parameter declarations in a function prototype (not part of a function
definition), the identifier has function prototype scope, which terminates at the end of the
function declarator.
gcc gives an appropriate warning for this usage in C code:
a.c:3:18: warning: ‘struct Test’ declared inside parameter list
void* foo(struct Test* t)
^
a.c:3:18: warning: its scope is only this definition or declaration, which is probably not what you want

qualified names in C++ class declaration

According to this page, the class name can be "optionally qualified". Thus, I expect the following code to compile:
struct ::globalSt {};
In MSVC 2013u4, I get an error:
Error 1 error C2039: 'globalSt' : is not a member of '`global namespace''
Am I misinterpreting the reference or is that a MSVC bug?
If you define a class with a qualified name, the name must have been previously declared. [class]/11
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.
In such cases, the nested-name-specifier of the class-head-name of the definition shall not begin with a
decltype-specifier.
For this to work the class must be already delcared. If you put a struct globalst; somewhere in your code before your definition it will work fine.
For example if you do
struct MyStruct;
then do
struct ::MyStruct {};
it should compile.
Tested with MSVC 2013.

Injected-class-names of class templates

Inspired by the code in this answer. Consider:
template<class>
class A { };
int main()
{
A<float> a(A<float>::A<int>());
return 0;
}
Is this code
ill-formed, because A<float>::A names the constructor (per §3.4.3.1 [class.qual]/p2) and cannot be used in this context (plus the <int> would complete fail to parse anyway), or
well-formed, with A<float>::A being the injected-class-name, used as a template-name (§14.6.1 [temp.local]), such that A<float>::A<int> means exactly the same as A<int>, and a being declared as a function (due to the most vexing parse)?
g++ says 1. clang says 2, and so does ICC 13. Which compiler is correct?
gcc is correct; your snippet is ill-formed!
// reduced testcase
template<class T>
class A { };
int main () {
A<float>::A<int> x; // ill-formed, bug in `clang` and `icc`
}
In the above reduced testcase we have a nested-name-specifier, A<float>::, followed by an unqualified-id A, which is then followed by some gibberish (<int>).
This is because the context in which the nested-name-specifier appears mandates that, during a look-up, function names are included (meaning that the constructor is found first, and the expression is ill-formed).
Relevant Bug Reports:
llvm.org/bugs/ - #8263; Incorrect constructor name resolution
How to circumvent the "problem"?
There are contexts in which member names that are looked up through a nested-name-specifier (that nominates a class) shall not include functions (hence, contexts where the constructor is not found), below are a few examples:
template<class T>
struct A {
typedef T value_type;
};
struct A<float>::A<int> x; // ok, context: elaborate-type-specifier
typename A<float>::A<int> (); // ok, context: [expr.type.conv]p1
A<float>::A::value_type x; // ok, context: nested-name-specifier
struct X : A<float>::A<int> { }; // ok, context: base-specifier
What does the Standard say?
3.4.3.1p2 Class members [class.qual]
In a lookup in which function names are not ignored88 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
in a using-declaration (7.3.3) that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id's template-name in the last component of the *nested-name-specicifier,
the name is instead considered to name the constructor of class C.
[ Note: ... ]
Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor or in a using-declaration.
88. Lookups in which function names are ignored include names appearing in a nested-name-specifier, an elaborated-type-specifier, or a base-specifier.
14.6.1p2 Locally declared names [temp.local]
Like normal (non-template) classes, class templates have an injected-class-name
(Clause 9). The injected-class-name can be used as a template-name or a
type-name.
When it is used with a template-argument-list, as a
template-argument for a template template-parameter, or as the final
identifier in the elaborated-type-specifier of a friend class template
declaration, it refers to the class template itself.
Otherwise, it is
equivalent to the template-name followed by the template-parameters of the
class template enclosed in <>.

Is my interpretation of the word 'first' in §3.3.2/6 correct?

In the snippet below I can understand (from §3.3.2/6 second bullet point) that the name B in the declaration struct B* p; is injected into the global namespace as a class-name.
struct A {
// struct B{};
int B;
struct B* p;
};
void f(B&) {}
int main()
{
A a;
f(*a.p);
}
§3.3.2/6:
The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:
for a declaration of the form
class-key attribute-specifier-seq opt identifier;
the identifier is declared to be a class-name in the scope that
contains the declaration, otherwise
for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a
class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is
declared in the smallest namespace or block scope that contains the
declaration. [ Note: These rules also apply within templates. — end
note ] [ Note: Other forms of elaborated-type-specifier do not
declare a new name, and therefore must refer to an existing type-name.
See 3.4.4 and 7.1.6.3. — end note ]
However if I uncomment the definition of struct B{}; inside struct A, what I said earlier with regard to the injection of name B into the global namespace, doesn't occur anymore, as the code doesn't compile. I believe this has to do with the word first (emphasis mine) above, since now the class-name B, in the declaration struct B* p; is no more its first declaration in its declarative region. Am I correct saying this?
Assuming my interpretation is correct, why is it that the class-name B is not injected in the global namespace in this case? Note that the nested class struct B{}; will be hidden inside A in this case, i.e., even if we change the declaration of function f to void f(A::B&) the code won't compile.
There is still one other point that isn't clear to me: what made the implementers to decide for the class-name injection into the namespace, or block scope, containing the elaborated-type-specifier, in the second bullet point above? That is, why didn't they leave the class-name declaration inside the class scope?
You're correct, that first keyword in §3.3.2/6 is also the reason for the following:
struct A {
struct B *p;
struct B{};
int b;
};
void f(B* arg) {
std::cout << std::is_same<decltype(arg), A::B*>::value; // not the same type
}
int main()
{
A a;
f(a.p);
}
why is it that the class-name B is not injected in the global namespace in this case?
As dyp pointed out, [basic.lookup.elab]/2 explains that 3.3.2 is only carried out in case no previous declaration could be found
If the elaborated-type-specifier is introduced by the class-key and
this lookup does not find a previously declared type-name, or if the
elaborated-type-specifier appears in a declaration with the form:
class-key attribute-specifier-seqopt identifier ;
elaborated-type-specifier is a declaration that introduces the
class-name as described in 3.3.2.the
Finally I tracked down this behavior to possibly be an inheritance from C99 6.7.2.3/p8
If a type specifier of the form
struct-or-union identifier
occurs
other than as part of one of the above forms, and no other declaration
of the identifier as a tag is visible, then it declares an incomplete
structure or union type, and declares the identifier as the tag of
that type.113)
113) A similar construction with enum does not exist.
I would say your interpretation is correct. When you uncomment struct B{};, the struct B* p; line will simply refer to that struct A::B.
To answer your question why, when you leave struct B {}; commented out, the name struct B is inserted into the global scope. I would say that's because the authors didn't want you to (somewhat) silently declare B as a member of A without using a member-specification for the name B.