How can I create an alias for a lengthy class name inside the definition of this class? I don't want it to be accessible outside the class definition. I can't get it to work with either typedef or using.
It's a class template in my case.
template<typename T>
class detailed_class_name{
typedef detailed_class_name<T> foo; // either one
using foo = detailed_class_name<T>; // of these
public:
foo(); // Error: ISO C++ forbids declaration of 'foo' with no type.
};
Any ideas?
You cannot. A constructor's id-expression, i.e. the name of the constructor in a class definition is according to [class.ctor]p1 (emphasis mine):
in a member-declaration that belongs to the member-specification of a class but is not a friend declaration, the id-expression is the injected-class-name of the immediately-enclosing class;
And what is an injected-class-name? According to [class]p2
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. [...]
And so the injected-class-name is the name of the class itself, and cannot be an alias.
This is further reinforced by this sentence in [class.ctor]p1:
The class-name shall not be a typedef-name.
Consider renaming the class if you don't even want to write a constructor because the name is too convoluted.
Related
This question already has answers here:
Can typedef names be used to declare or define constructors?
(2 answers)
Using class alias for its constructor definition [duplicate]
(1 answer)
Closed 3 months ago.
Take this class:
class Foo {
public:
using MyType = Foo;
MyType* m = nullptr; //ok
MyType* functor() { //ok
return nullptr;
}
MyType() = default; //error
~MyType() = default; //error
};
Why can you use alias names for members, but not for constructors or destructors?
Since this is tagged language-lawyer, lets see what the standard has to say.
From [class.ctor.gen], emphasis added:
A declarator declares a constructor if it is a function declarator
([dcl.fct]) of the form ptr-declarator ( parameter-declaration-clause
) noexcept-specifieropt attribute-specifier-seqopt where the
ptr-declarator consists solely of an id-expression, an optional
attribute-specifier-seq, and optional surrounding parentheses, and the
id-expression has one of the following forms:
(1.1) in a friend declaration ([class.friend]), the id-expression is a qualified-id that names a constructor ([class.qual]);
(1.2) otherwise, in a member-declaration that belongs to the member-specification of a class or class template, the id-expression
is the injected-class-name ([class.pre]) of the immediately-enclosing
entity;
(1.3) otherwise, the id-expression is a qualified-id whose unqualified-id is the injected-class-name of its lookup context.
For the subject of why a name alias is not valid to declare constructor, especially (1.2) is of relevance. It states that the id-expression is the injected-class-name [..]. The latter is defined in [class.pre]:
A class is a type. Its name becomes a class-name ([class.name]) within its scope.
[..]
The class-name is also bound in the scope of the class (template) 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. [..]
This states very plainly, that the injected-class-name is exactly the name used to declare the class in the first place. There is no allowance in the wording for aliases.
This follows the same mechanism that you see in a class template:
template <typename T> class C { C foo(); };
Now, there is no class named C, C is a template, so you would normally expect to have to say C<T> foo();. But as stated in [class.pre], a class-name is introduced in the scope of the class definition which refers to the actual type. So, even if there is no class C anywhere, the class-name C can be used as a shorthand for C<T> and in the case of constructors and destructors, is the only way to refer to it because their declaration does not allow you to use any type-id, but only class-name.
m is a pointer to an instance of a type, which is aliased. OK.
functor() returns a pointer to an instance of a type, which is aliased. OK.
But a constructor/destructor is not itself a type, so you can't use a type alias for them. They must be named after the type they belong to. That is just the way the syntax works
struct A{};
int A;
struct A a;
struct A::A b;
The last two declarations above are equivalent.They both declare objects of type A. Where in the Standard can I find or deduce this?
[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.
I.e. A::A::A::A refers to A as well. In some contexts, A::A could name the constructor instead, though - [class.qual]/2 covers this, and its note even addresses your example:
In a lookup in which function names are not ignored33 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-specifier,
the name is instead considered to name the constructor of class C. [ Note: For example, the constructor is not an acceptable
lookup result in an elaborated-type-specifier so the constructor
would not be used in place of the injected-class-name. — end
note ]
33) Lookups in which function names are ignored include
names appearing in a nested-name-specifier, an elaborated-type-
specifier, or a base-specifier.
So in a statement such as
A::A a;
Function names are not ignored when looking up A::A, and thus the code is ill-formed as A::A refers to the constructor. However, in
struct B : A::A {};
struct A::A a;
Everything is fine as function names are ignored in base-specifiers and elaborated-type-specifiers.
The following simple code example causes some doubts for me:
#include <iostream>
using namespace std;
struct A
{
int a;
A(int a)
{
A::a = a; //It is unclear, because in that case we're applying
//scope resolution operator to A and does.
this -> a = a; //It is clear, because this points to the current object.
}
};
int main()
{
A a(4);
cout << a.a;
}
demo
I know that the section 3.4.3.1/3 says:
A class member name hidden by a name in a nested declarative region or
by the name of a derived class member can still be found if qualified
by the name of its class followed by the :: operator.
But it doesn't specify that the name looked up with "Qualified name lookup" (e.g. A::a in my case) inside the member function shall denote a member of current object.
I'm looking for relevant reference in the Standard.
When searching for something specific to nonstatic class member functions, you should look first at the subclause governing...nonstatic class member functions. §9.3.1 [class.mfct.non-static]/p3:
When an id-expression (5.1) that is not part of a class member access
syntax (5.2.5) and not used to form a pointer to member (5.3.1) is
used in a member of class X in a context where this can be used
(5.1.1), if name lookup (3.4) resolves the name in the id-expression
to a non-static non-type member of some class C, and if either the
id-expression is potentially evaluated or C is X or a base class of X,
the id-expression is transformed into a class member access expression
(5.2.5) using (*this) (9.3.2) as the postfix-expression to the left of
the . operator. [ Note: If C is not X or a base class of X, the class
member access expression is ill-formed. —end note ] Similarly during
name lookup, when an unqualified-id (5.1) used in the definition of a
member function for class X resolves to a static member, an enumerator
or a nested type of class X or of a base class of X, the
unqualified-id is transformed into a qualified-id (5.1) in which the
nested-name-specifier names the class of the member function.
C++.11 §5.1.1¶8
A nested-name-specifier that denotes a class, optionally followed by the keyword template (14.2), and then followed by the name of a member of either that class (9.2) or one of its base classes (Clause 10), is a qualified-id; 3.4.3.1 describes name lookup for class members that appear in qualified-ids. The result is the member. The type of the result is the type of the member. The result is an lvalue if the member is a static member function or a data member and a prvalue otherwise.
I thought that every declaration is definition because there was the following quote from standard:
A declaration is a definition unless %restrictions%.
But my assumption is not true. Actually, applying ODR we have that the following program
class A;
class A;
int main(){ }
is ill-formed. But it is not true. I can't to find part of standard which permit to redeclare class type in the declarative region.
Yes, "a declaration is a definition unless %restrictions%" is true. Have you read the restrictions? One of them is:
it is a class name declaration
So class A; is not a definition because it is covered by one of the restrictions.
Just to clarify, quoting C++11, [basic.def]§2
Your quote (from C++11 3.1/2) answers the general question: "unless %restrictions%" means that not every declaration is a definition. It's only a definition if none of those restrictions apply.
If you read those restrictions, you'll find
it is a class name declaration
which answers your specific question. class A; is a class name declaration, but not a definition.
I can't to find part of standard which permit to redeclare class type in the declarative region.
In general, you can declare an entity multiple times in the same declarative region, per C++11 3.3.1/4
Given a set of declarations in a single declarative region, each of which specifies the same unqualified name, they shall all refer to the same entity, or [other cases not relevant here]
The paragraph § 3.1.2 states that
A declaration is a definition unless it declares a function without
specifying the function’s body (8.4), it contains the extern specifier
(7.1.1) or a linkage-specification 25 (7.5) and neither an initializer
nor a function- body, it declares a static data member in a class
definition (9.2, 9.4), it is a class name declaration (9.1), it is an
opaque-enum-declaration (7.2), it is a template-parameter (14.1), it
is a parameter-declaration (8.3.5) in a function declarator that is
not the declarator of a function-definition, or it is a typedef
declaration (7.1.3), an alias-declaration (7.1.3), a using-declaration
(7.3.3), a static_assert-declaration (Clause 7), an attribute-
declaration (Clause 7), an empty-declaration (Clause 7), or a
using-directive (7.3.4).
Here
class A;
class A;
int main(){ }
it is a class name declaration.
In a statement
A declaration is a definition unless %restrictions%.
the %restrictions%. part is important.
I thought that every declaration is definition
Let's prove that this is not true by a contradiction. So assume this is true. Then because we can have many redeclaration and every of this declarations is definition - we can have many redefinitions, right? But C++ Standard n3337 § 3.2/1 says
No translation unit shall contain more than one definition of any
variable, function, class type, enumeration type, or template.
what contradicts our assumption and thus this is not true that every declaration is definition.
Consider the case of:
Header1.h:
class A;
void function1(A * a);
Header2.h:
class A;
void function2(A * a);
main.cpp
#include "Header1.h"
#include "Header2.h"
#include "A.h" // header file defining A
int main()
{
}
what this really looks like to the compiler is:
class A;
void function1(A * a);
class A;
void function2(A * a);
class A { /* definition from A.h */ };
int main()
{
}
class A {
public: enum class { HELLO, WORLD };
};
Having known that, inside a class, declaring a simple enum (rather than enum class) is a better idea, because it's already typed with the class identification. But still above statement is a valid C++0x signature. Now how to access an unnamed enum class outside ?
int i = A::HELLO; // error: ‘HELLO’ is not a member of ‘A’
Actually, that is not valid. The C++0x FDIS says (9.2p1)
Except when used to declare friends (11.3) or to introduce the name of a member of a base class into a derived class (7.3.3), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.
In your case, no enumerator name is introduced into the class' scope and no enumeration name is introduced either. So, no member name at all is introduced by that member-declaration.
EDIT: And actually, there's a more direct prohibition of the enumeration declaration. 7.2p2:
The optional identifier shall not be omitted in the declaration of a scoped enumeration.