I was trying to validate this statement (my emphasis) in paragraph §5.1.1/8 (page 87) of the C++11 Standard
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.
with the following snippet:
#include <iostream>
namespace N {
class A {
public:
int i;
void f();
};
}
int main()
{
std::cout << &N::A::f << '\n';
std::cout << &N::A::i << '\n';
}
clang and gcc compile this code and VS2013 requires the definition of the member function f.
All three of them print
1
1
but I have no idea where these numbers come from.
live example
According to the paragraph highlighted above the expressions N::A::f is a prvalue, as f is not a static member function. Nonetheless, I was able to take its address in the code.
At the same time, in §5.3.1/3 one reads (emphasis mine):
The result of the unary & operator is a pointer to its operand. The
operand shall be an lvalue or a qualified-id. If the operand is a
qualified-id naming a non-static member m of some class C with type T,
the result has type “pointer to member of class C of type T” and is a
prvalue designating C::m.
which gives the impression that neither N::A::f nor N::A::i are lvalues, as they are qualified-ids.
but I have no idea where these numbers come from.
Pointer-to-members aren't pointers. No operator<< can output their original value, the best and only match is the one that outputs bool values. Thus they are converted to bool (which obviously yields true) and the output is 1. Try to insert std::boolalpha and check the output again.
Nonetheless, I was able to take its address in the code.
How is that a surprise to you? You quoted the part that allows and explains this exact construct. It clearly states that taking the adress of a qualified-id that names a non-static member designates that member.
qualified-ids are not only lvalues or rvalues. It completely depends on context. If they designate non-static members from outside that members class or any subclass of it, they have to be prvalues as they don't designate any specific object but rather a value (or information, in other words -- the type and an offset).
Related
Both gcc and clang accept the following code, and I'm trying to figure out why.
// c++ -std=c++20 -Wall -c test.cc
#include <concepts>
struct X {
int i;
};
// This is clearly required by the language spec:
static_assert(std::same_as<decltype(X::i), int>);
// This seems more arbitrary:
static_assert(std::same_as<decltype((X::i)), int&>);
The first static_assert line makes sense according to [dcl.type.decltype]:
otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), 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;
- https://timsong-cpp.github.io/cppwp/n4861/dcl.type.decltype#1.3
X::i is a valid id-expression in an unevaluated context, so its decltype should be the declared type of i in X.
The second static_assert has me stumped. There's only one clause in [dcl.type.decltype] in which a parenthesized expression yields an lvalue reference: it must be that both compilers consider X::i to be an expression of lvalue category. But I can't find any support for this in the language spec.
Obviously if i were a static data member then X::i would be an lvalue. But for a non-static member, the only hint I can find is some non-normative language in [expr.context]:
In some contexts, unevaluated operands appear ([expr.prim.req], [expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.simple], [temp.pre], [temp.concept]).
An unevaluated operand is not evaluated.
[ Note: In an unevaluated operand, a non-static class member may be named ([expr.prim.id]) and naming of objects or functions does not, by itself, require that a definition be provided ([basic.def.odr]).
An unevaluated operand is considered a full-expression.
— end note
]
- https://timsong-cpp.github.io/cppwp/n4861/expr.prop#expr.context-1
This suggests decltype((X::i)) is a valid type, but the definition of full-expression doesn't say anything about value categories. I don't see what justifies int& any more than int (or int&&). I mean an lvalue is a glvalue, and a glvalue is "an expression whose evaluation determines the identity of an object." How can an expression like X::i--which can't be evaluated at all, let alone determine the identity an object--be considered a glvalue?
Are gcc and clang right to accept this code, and if so what part of the language specification supports it?
remark:
StoryTeller - Unslander Monica's answer makes even more sense in light of the fact that sizeof(X::i) is allowed and that decltype((X::i + 42)) is a prvalue.
How can an expression like X::i--which can't be evaluated at all, let alone determine the identity an object--be considered a glvalue?
That's not entirely true. That expression can be evaluated (following a suitable transformation in the right context).
[class.mfct.non-static]
3 When an id-expression that is not part of a class member access syntax and not used to form a pointer to member ([expr.unary.op]) is used in a member of class X in a context where this can be used, if name lookup 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 using (*this) as the postfix-expression to the left of the . operator.
So we can have something like
struct X {
int i;
auto foo() const { return X::i; }
};
Where X::i is transformed into (*this).X::i. Which is explicitly allowed on account of
[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 ...
And that meaning of (*this).X::i is always an lvalue denoting the class member i.
So you see, in the contexts where X::i can be evaluated, it always produces an lvalue. So decltype((X::i)) in those context would need to be an lvalue reference type. And while outside of class scope X::i cannot generally be used in an expression, it's still not entirely arbitrary to say its value category is an lvalue. We are just expanding the region of definition a bit (which doesn't contradict the standard, since the standard doesn't define it).
How can an expression like `X::i--which can't be evaluated at all, let alone determine the identity an object--be considered a glvalue?
Ignoring the misuse of «result», it is [expr.prim.id.qual]/2:
A nested-name-specifier that denotes a class, optionally followed by the keyword template ([temp.names]), and then followed by the name of a member of either that class ([class.mem]) or one of its base classes, is a qualified-id; ... The result is an lvalue if the member is a static member function or a data member and a prvalue otherwise.
The value category of an expression is not determined by the «definition» in [basic.lval], like «an expression whose evaluation determines the identity of an object», but specified for each kind of expression explicitly.
The root of your question seems to be the difference between decltype(X::i) and decltype((X::i)). Why does (X::i) yield a int&? See:
https://timsong-cpp.github.io/cppwp/n4861/dcl.type.decltype#1.5
otherwise, if E is an lvalue, decltype(E) is T&, where T is the type of E;
However, the key point here is that the fact that it yields a T& doesn't really matter:
https://timsong-cpp.github.io/cppwp/n4861/expr.type#1
If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression. [ Note: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see [basic.life]). — end note ]
What "justifies" it then? Well, when doing decltype(X::i) we're concerned primarily with what the type of X::i is and not its value category or its properties when treated as an expression. However, (X::i) is there if we do care.
I'm trying to understand the consistency in the error that is thrown in this program:
#include <iostream>
class A{
public:
void test();
int x = 10;
};
void A::test(){
std::cout << x << std::endl; //(1)
std::cout << A::x << std::endl; //(2)
int* p = &x;
//int* q = &A::x; //error: cannot convert 'int A::*' to 'int*' in initialization| //(3)
}
int main(){
const int A::* a = &A::x; //(4)
A b;
b.test();
}
The output is 10 10. I labelled 4 points of the program, but (3) is my biggest concern:
x is fetched normally from inside a member function.
x of the object is fetched using the scope operator and an lvalue to the object x is returned.
Given A::x returned an int lvalue in (2), why then does &A::x return not int* but instead returns int A::*? The scope operator even takes precedence before the & operator so A::x should be run first, returning an int lvalue, before the address is taken. i.e. this should be the same as &(A::x) surely? (Adding parentheses does actually work by the way).
A little different here of course, the scope operator referring to a class member but with no object to which is refers.
So why exactly does A::x not return the address of the object x but instead returns the address of the member, ignoring precedence of :: before &?
Basically, it's just that the syntax &A::x (without variation) has been chosen to mean pointer-to-member.
If you write, for example, &(A::x), you will get the plain pointer you expect.
More information on pointers-to-members, including a note about this very property, can be found here.
The C++ standard doesn't explicitly specify operator precedence; it's possible to deduce operator precedence implicitly from the grammar rules, but this approach fails to appreciate the occasional special case like this which doesn't fit into a traditional model of operator precedence.
[expr.unary.op]/3:
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a
qualified-id naming a non-static or variant member m of some class C with type T, the result has type 'pointer to member of class C of type
T' and is a prvalue designating C::m. Otherwise, if the type of the expression is T, the result has type 'pointer to T' and is a prvalue that is the address of the designated object or a pointer to the designated function.
/4:
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [ Note:
that is, the expression &(qualified-id), where the qualified-id is enclosed in parentheses, does not form an expression of type 'pointer to member'.
[expr.prim.general]/9:
A nested-name-specifier that denotes a class, optionally followed by the keyword template, and then followed by the name of a member of either that class or one of its base classes, is a qualified-id.
What it all adds up to is that an expression of the form &A::x has the type "pointer to member x of class A" if x is a non-static member of a non-union class A, and operator precedence has no influence on this.
5.1.1/2 is stated that:
The keyword this names a pointer to the object for which a
non-static member function (9.3.2) is invoked or a non-static data
member’s initializer (9.2) is evaluated.
And:
Unlike the object expression in other contexts, *this is not
required to be of complete type for purposes of class member access
(5.2.5) outside the member function body.
The following code prints 8:
#include <cstddef>
#include <iostream>
struct Test
{
std::size_t sz = sizeof(this->sz);
};
int main()
{
std::cout << Test{}.sz;
}
5.3.3 says:
The operand is either an expression, which is an unevaluated operand
(Clause 5), or a parenthesized type-id. The sizeof operator shall
not be applied to an expression that has function or incomplete
type...
sizeof this->sz has the same result.
Is this-> considered a no-op in this case and it's essentially equivalent to sizeof(sz)?
Is this-> considered a no-op in this case and it's essentially equivalent to sizeof(sz)?
That's right.
The type of this->sz is std::size_t, a complete type in that context.
The type of *this is not complete here, but you quoted the passage stating why that doesn't matter and we can go straight through to analysing sz specifically.
As such, the this-> had no actual effect on the semantics of the expression, either for better or for worse.
As Sergey said, there is one case where using this-> for member access makes a difference (template bases!), and this is not one of them.
Apparently clang thinks decltype(this) is a pointer to the cv-qualified class, while gcc thinks it is a const reference to a pointer to the cv-qualified class. GCC only thinks decltype(&*this) is a pointer to the cv-qualified class. This has some implications when it is used as the typename for a template. Consider a hypothetical example:
template<typename T>
class MyContainer {
/* ... */
template<typename ContainerPtr>
class MyIterator {
ContainerPtr container;
/* ... */
};
auto cbegin() const
-> MyIterator<decltype(&*this)> { return { /* ... */ }; }
auto cend() const
-> MyIterator<decltype(this)> { return { /* ... */ }; }
};
In this example, one implements a custom container of T. Being a container, it supports iterators. In fact, two kinds of iterators: iterators and const_iterators. It would not make sense to duplicate the code for these two, so one could write a template iterator class, taking either a pointer to the original class MyContainer<T> * or a pointer to the const version MyContainer<T> const *.
When cbegin and cend are used together, gcc errors out, saying it deduced conflicting types, while clang just works fine.
this is a prvalue, so decltype(this) should always be plain X* (or X cv* / cv X*). The addition of const& seems to be a bug in GCC (tested with g++ 4.8.1), which happens only for a class template (not for a "plain" class) and only inside the trailing return type (not inside the body of the member function): demo. This seems to be fixed in GCC 4.9 (experimental), you can test here.
Okay, here is what I found in the standard (N3337) though:
7.1.6.2 Simple type specifiers [dcl.type.simple]
4 The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an
unparenthesized class member access (5.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;
— otherwise, if e is an xvalue, decltype(e) is T&&, where
T is the type of e; — otherwise, if e is an lvalue,
decltype(e) is T&, where T is the type of e; —
otherwise, decltype(e) is the type of e. The operand of the
decltype specifier is an unevaluated operand (Clause 5).
and
5.1.1 General [expr.prim.general]
3 If a declaration declares a member function or member function
template of a class X, the expression this is a prvalue of type
“pointer to cv-qualifier-seq X” between the optional cv-qualifer-seq
and the end of the function-definition, member-declarator, or
declarator. It shall not appear before the optional cv-qualifier-seq
and it shall not appear within the declaration of a static member
function (although its type and value category are defined within a
static member function as they are within a non-static member
function). [ Note: this is because declaration matching does not occur
until the complete declarator is known. — end note ] Unlike the object
expression in other contexts, *this is not required to be of complete
type for purposes of class member access (5.2.5) outside the member
function body. [ Note: only class members declared prior to the
declaration are visible. — end note ]
The previous reference to §9.3.2 is an error, since that deals with the body of a member function, as pointed out below in a comment by MWid.
9.3.2 The `this` pointer [class.this]
1 In the body of a non-static (9.3) member function, the
keyword `this` is a prvalue expression whose value is the address of
the object for which the function is called. The type of `this` in a
member function of a class `X` is `X*`. If the member function is
declared `const`, the type of `this` is `const X*`, if the member
function is declared `volatile`, the type of `this` is `volatile X*`,
and if the member function is declared `const volatile`, the type of
`this` is `const volatile X*`.
So it looks like gcc is wrong.
I've been experimenting with function types in C++. Note that I don't mean pointer-to-function types like:
typedef void (*voidFuncPtr)();
but the more exotic:
typedef void (voidFunc)();
I didn't expect the following code to compile, but surprisingly it did:
template<voidFunc func>
class funcClass
{
public:
void call() { func(); };
};
void func()
{ }
void Test()
{
funcClass<func> foobar;
foobar.call();
}
however, if I try adding the following to funcClass:
voidFuncPtr get() { return &func; }
I get the error Address expression must be an lvalue or a function designator
My first question here is: what kind of black magic is the compiler using to pretend that a func type is something it can actually pass around an instance of? Is it just treating it like a reference? Second question is: if it can even be called, why can't the address of it be taken? Also, what are these non-pointer-to function types called? I only discovered them because of boost::function, and have never been able to find any documentation about them.
§14.1.4 of the Standard says:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
— integral or enumeration type,
— pointer to object or pointer to function, [this is what yours is]
— lvalue reference to object or lvalue reference to function,
— pointer to member,
— std::nullptr_t.
And §14.1.6 says
A non-type non-reference template-parameter is a prvalue. It shall not
be assigned to or in any other way have its value changed. A non-type
non-reference template-parameter cannot have its address taken. When a
non-type non-reference template-parameter is used as an initializer
for a reference, a temporary is always used.
So that explains the two behaviours you are seeing.
Note that func is the same as &func (§14.3.2.1):
[A non-type template parameter can be] a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a
function with external or internal linkage, including function
templates and function template-ids but excluding non-static class
members, expressed (ignoring parentheses) as & id-expression, except
that the & may be omitted if the name refers to a function or array
and shall be omitted if the corresponding template-parameter is a
reference; or...
So it's just a function pointer.
Given that the code compiles without the address-of operator and pointers (including to functions and member functions) are valid template arguments, it seems the compiler considers voidFunc to be a function pointer type, i.e., the decayed version of the type. The rules for this didn't change between C++ 2003 and C++ 2011.