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.
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
I'm not sure to perfectly understand [dcl.type]/4.3:
For an expression e, the type denoted by decltype(e) is defined as follows:
[...]
(4.3) otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
[...]
For me, the emphasized part both apply to id-expression and class member access, right?
Playing with my favorite compiler, I get the following.
✓ Accepted by the compiler
namespace N { void f() {} }
using type = decltype(N::f);
type* pf = N::f;
Ok I guess; N::f is an unparenthesized id-expression and does not name a set of overloaded functions.
✗ Rejected by the compiler
namespace N { void f() {} void f(int) {} }
using type = decltype(N::f); // error: decltype cannot resolve address of overloaded function
type* pf = N::f;
Ok; N::f does name a set of overloaded functions.
✗ Rejected by the compiler
struct S { void f(){} };
using type = decltype(S::f); // error: invalid use of non-static member function 'void S::f()'
type* pf = &S::f;
Hum? S::f would name a set of one overloaded function?
All in all, is my understanding of [dcl.type]/4.3 just bad? is gcc trunk wrong? both? none? kamoulox?
The simple reason is that the use of S::f is constrained for class members.
[expr.prim.id]
2 An id-expression that denotes a non-static data member or
non-static member function of a class can only be used:
as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or
to form a pointer to member ([expr.unary.op]), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
The last bullet, the one related to your code, only applies to non-static data member. There is no provision for functions.
I can only speculate as to why it's not allowed, though I previously asked that question.
It's worth a note that decltype(&S::f) works here as pointer-to-member-function type,
again unless the f names a set of overloaded (member) functions.
The function type itself can be extracted from the pointer-to-member-function type.
If the member function is cv-or-ref qualified then it has an abominable function type.
The std traits are lacking here - a library like Boost.CallableTraits helps.
I have a class A which has two static variables. I'd like to initialize one with another, unrelated static variable, just like this:
#include <iostream>
class A
{
public:
static int a;
static int b;
};
int A::a = 200;
int a = 100;
int A::b = a;
int main(int argc, char* argv[])
{
std::cout << A::b << std::endl;
return 0;
}
The output is 200. So, could anyone tell me why?
That's correct according to the lookup rules. [basic.lookup.unqual]/13 says:
A name used in the definition of a static data member of class X
(after the qualified-id of the static member) is looked up as if the
name was used in a member function of X. [ Note: [class.static.data]
further describes the restrictions on the use of names in the
definition of a static data member. — end note ]
Since the unqualified a is looked up as if you are inside a member function, it must find the member A::a first. The initialization order of A::a and A::b doesn't affect the lookup, though it affects how well defined the result is.
So, could anyone tell me why?
This is clearly stated in basic.scope.class/4, emphasis mine:
The potential scope of a declaration that extends to or past the end
of a class definition also extends to the regions defined by its
member definitions, even if the members are defined lexically outside
the class (this includes static data member definitions, nested class
definitions, and member function definitions, including the member
function body and any portion of the declarator part of such
definitions which follows the declarator-id, including a
parameter-declaration-clause and any default arguments).
Thus, when you have
int A::a = 200;
int a = 100;
int A::b = a; // note the '::' scope resolution operator
// OUTPUT: 200
a actually refers to A::a because the class scope is extended by A::b.
Unlike if you have:
int A::a = 200;
int a = 100;
int b = a; // note b is not A::b
// i.e. without the '::', scope resolution operator
// OUTPUT: 100
a would refer to the (global) ::a since b here is not a member of class A,i.e. no class scope extension.
c++draft/class.static
If an unqualified-id is used in the definition of a static member following the member's declarator-id, and name lookup ([basic.lookup.unqual]) finds that the unqualified-id refers to a static member, enumerator, or nested type of the member's class (or of a base class of the member's class), the unqualified-id is transformed into a qualified-id expression in which the nested-name-specifier names the class scope from which the member is referenced. [ Note: See [expr.prim.id] for restrictions on the use of non-static data members and non-static member functions. — end note ]
It says the unqualified-id is transformed into a qualified-id expression in your situation.
int A::b = a;
You can set qualified-id but has no nested-name-specifier like this.
int A::b = ::a;
Because the name look up resolves the a as A::a. If you want to do this you will need to resolve the scope manually:
int A::b = ::a;
// ^ Global scope resolution
Live Example
I am not asking decltype((x)), I know how it works.
According to the draft N4687, § 10.1.7.2
4 For an expression e, the type denoted by decltype(e) is defined as follows:
...
(4.2) — otherwise, if e is an unparenthesized id-expression or an unparenthesized class
member access (8.2.5), decltype(e) is the type of the entity named by e. If
there is no such entity, or if e names a set of overloaded functions, the
program is ill-formed;
...
And example
struct A { double x; };
const A* a = new A();
decltype(a->x) x3; // type is double
My question is,
a->x is const double, but why does x3 is double? where does the const go?
BTW, what is decltype(e) is the type of the entity named by e meaning exactly?
The standard seems ambiguous im this area.
An entity is a value, object, reference, function, enumerator, type, class member, bit-field, template, template specialization, namespace, or parameter pack.
The expression a->x can be said to name a member x of struct A, which has type double. The same expression can also be said to name an object which has type const double. Both of these things are entities. The normative text doesn't make it absolutely clear that the intended interpretation is the first one, it can only be inferred from the example.
N4687 [dcl.type.simple] ¶4.2 ...if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e.
A class member access is either . or ->, according to [expr.ref].
[basic] ¶3 An entity is a value, object, reference, function, enumerator, type, class member, bit-field, template, template specialization, namespace, or parameter pack.
¶4 A name is a use of an identifier, operator-function-id, literal-operator-id, conversionfunction-id, or template-id that denotes an entity or label.
¶5 Every name that denotes an entity is introduced by a declaration.
There is an ambiguity here: a->x is both a class member and an object (a member subobject). The important thing to note is that decltype(e) is the type of the entity named by e. The only kinds of entities that can be named are those that are introduced by declarations (¶5). A member subobject does not have a name in this sense, as it is not declared. That leaves the only other alternative that decltype(x->a) must be the type of the class member (not the object member).
The "entity" named by a class member access expression is that class member, in this case, A::x.
The type of A::x is double.
Why can't I do this:
class Foo {
void fn();
using fn_t = decltype(fn); //call to non-static member function without an object argument
};
But I can do
class Foo {
static void fn();
using fn_t = decltype(fn);
};
This SO post claims:
Within unevaluated operands (operands of decltype, sizeof, noexcept, ...) you can name nonstatic data members also outside of member functions
fn is a valid id-expression denoting a non-static member function. §5.1.1 [expr.prim.general]/p13 (footnote omitted):
An id-expression that denotes a non-static data member or non-static
member function of a class can only be used:
as part of a class member access (5.2.5) in which the object expression refers to the member’s class or a class derived from that
class, or
to form a pointer to member (5.3.1), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
§7.1.6.2 [dcl.type.simple]/p4:
The operand of the decltype specifier is an unevaluated operand
(Clause 5).
Since decltype is not one of the few contexts in which an id-expression denoting a non-static member function may be used, the program is ill-formed.