Mutation of a mutable data-member via pointer-to-member - c++

The Standard provide the following note:
[ Note: it is not possible to use a pointer to member that refers to a
mutable member to modify a const class object. For example,
struct S {
S() : i(0) { }
mutable int i;
};
void f()
{
const S cs;
int S::* pm = &S::i; // pm refers to mutable member S::i
cs.*pm = 88; // ill-formed: cs is a const object
}
-end note]
But we can use just object-expression to modify const object which has a mutale data member.
#include <iostream>
struct A
{
A(){ }
mutable int a;
};
const A a;
int main()
{
a.a = 4;
std::cout << a.a; //4
}
DEMO
But ISO/IEC Derictive tells
Notes and examples integrated in the text of a document shall only be
used for giving additional information intended to assist the
understanding or use of the document. They shall not contain
requirements ("shall"; see 3.3.1 and Table H.1) or any information
considered indispensable for the use of the document [...]
Which means the note I provide at the beginning of my Q is not a requirement.
I'm looking for a normative requirement explcitly precludes such using.

Just before that note you will find the following:
The restrictions on cv-qualification, and the manner in which the cv-qualifiers of the operands are combined to produce the cv-qualifiers of the result, are the same as the rules for E1.E2 given in 5.2.5
Jump up to 5.2.5 and you'll find this:
If E2 is a non-static data member and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue; otherwise E1.E2 is an xvalue. Let the notation vq12 stand for the “union” of vq1 and vq2; that is, if vq1 or vq2 is volatile, then vq12 is volatile. Similarly, let the notation cq12 stand for the “union” of cq1 and cq2; that is, if cq1 or cq2 is const, then cq12 is const. If E2 is declared to be a mutable member, then the type of E1.E2 is “vq12 T”. If E2 is not declared to be a mutable member, then the type of E1.E2 is “cq12 vq12 T”.
The union of the const qualifiers in cs.*pm is const, the exception for mutable members doesn't apply to pointers.
It's easier to understand if you consider that storage class specifiers are not part of the type, so how would the compiler be able to distinguish between mutable and non-mutable pointers to member?
struct S;
void f(const S& s, int S::* pm)
{
s.*pm = 1; // How do I know if pm points to a mutable member? S isn't even defined!
}
Simply put, there is no such thing as a pointer to a mutable member, just as there is no such thing as a pointer to a static member, the pointed type's storage class is unknown (the type can only be qualified by const and / or volatile).

Related

Could we access member of a non-existing class type object?

In the c++ standard, in [basic.lval]/11.6 says:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:[...]
an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),[...]
This sentence is part of the strict-aliasing rule.
Does it allow us to alias through class member access?
class A{ //"casted to" aggregate
int i;
float g;
};
int j=10;
int l = reinterpret_cast<A*>(&j)->i;
According to the standard, an object value is only accessed if the object is subject to lvalue-to-rvalue-conversion [conv.lval]/2.
[expr.ref] does not stipulate that the object-expression must refer to an object of that type, only that the glvalue must have class type (the object-expression is the expression at the left of the dot "."). Nevertheless there is a word that recurrently appears in sentence related to member access which may imply constraint on the program that I overlooked. For example [expr.ref]/4.1:
If E2 is a static data member and the type of E2 is T, then E1.E2 is an lvalue; the expression designates the named member of the class.
Could "designates" means that this member is within its lifetime and that I cannot use class member access to perfom aliasing? Or is it allowed by [basic.lval]/11.6?
For non-static data member, the word is:
If E2 is a non-static data member and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the named member of the object designated by the first expression.
Clearly the object designated by *reinterpret_cast<A*>(&j), i.e. j, has no member, so the behavior is undefined by omission.

Returning a member from an rvalue object

Lets take two structs/classes
struct C1{
C1(){};
C1(C1&){std::cout<<"copy"<<std::endl;}
C1(C1&&){std::cout<<"move"<<std::endl;}};
struct C2{
C1 c;
C2(){};
C1 get1(){return c;}
C1 get2(){return std::move(c);}};
And than
C1 a1=C2().c;
C1 a2=C2().get1();
C1 a3=C2().get2();
the output is
move
copy
move
Question
We know that members of rvalues are rvalues themselves. This is why with a1, the move constructor is called. Why than, is the copy constructor called in the case of a2. We are returning an rvalue from a function.
To put it differently, std::move casts to an rvalue. But, as a member of an rvalue, c, is already an rvalue. Why than is there a difference between the behavior with a2 and a3?
Good question. The dull answer is that there's just no no rule in the C++ spec that says that returning a member from a dying object like this automatically moves it.
You may be interested in what's called rvalue reference to this. It let's you overload on && and & such that you can manually implement the behaviour you expected:
struct C2{
C1 c;
C2(){};
C1 get1() & { std::cout << "&" << std::endl; return c;}
C1 get1() && { std::cout << "&&" << std::endl; return std::move(c);}
C1 get2(){return std::move(c);}
};
Now
C1 a2=C2().get1();
prints
&&
move
Cool, but quite rare.
Related:
What is "rvalue reference for *this"?
Is a member of an rvalue structure an rvalue or lvalue?
We know that members of rvalues are rvalues themselves.
Yes this is true, as states [expr.ref]/4.2 (emphasis mine):
If E2 is a non-static data member and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue; otherwise E1.E2 is an xvalue. Let the notation vq12 stand for the “union” of vq1 and vq2; that is, if vq1 or vq2 is volatile, then vq12 is volatile. Similarly, let the notation cq12 stand for the “union” of cq1 and cq2; that is, if cq1 or cq2 is const, then cq12 is const. If E2 is declared to be a mutable member, then the type of E1.E2 is “vq12 T”. If E2 is not declared to be a mutable member, then the type of E1.E2 is “cq12 vq12 T”.
And also, from [expr.ref]/4.5:
If E2 is a member enumerator and the type of E2 is T, the expression E1.E2 is a prvalue. The type of E1.E2 is T.
So far so good. You can only get an lvalue if E1 is an lvalue itself, otherwise it's an xvalue or a prvalue.
But, as a member of an rvalue, c, is already an rvalue.
This is where your assumptions are wrong.
From [class.this]/1 (emphasis mine)
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*.
this is a prvalue of type X*, and dereferencing a pointer of type X yield an lvalue of type X.
Since accessing a member inside a member function is equivalent to (*this).m, then m is accessed through an lvalue of type X.
So your code is equivalent to:
C1 get1() { return (*this).c; }
// lvalue ----^ ^--- must be an lvalue too then.
Since this is always the same type, then even when using a function ref-qualifier the expression c inside a member function will always be an lvalue:
C1 get1() && { return (*this).c; }
// ^---- lvalue again, accessing through a pointer

Is a data member of a temporary object an xvalue in C++11?

#include <vector>
using namespace std;
struct A
{
vector<int> coll;
};
void f(const vector<int>&){}
void f(vector<int>&&){}
int main()
{
f(A().coll); // Is "A().coll" an xvalue?
}
Does C++11 guarantee f(A().coll) will call void f(vector<int>&&)?
Yes. C++14 standard, §5.2.5/4.2, given E1.E2:
If E2 is a non-static data member and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue; otherwise E1.E2 is an xvalue.
Pedantically, originally C++11 classified this as a prvalue, but such classification was meaningless so it was changed. If the change was applied by a defect report, though, then it's retroactive — the published C++11 standard document N3290 is wrong and the C++14 document defines C++11 instead. That's likely to be the case, as otherwise would require compilers to implement a subtle difference in behavior between -std=c++11 and -std=c++14. I'm lazy to search through DRs right now.

What is the type of decltype(this) in C++?

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.

Does `decltype` give me an object's static type, or its runtime type?

[C++11: 7.1.6.2/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).
The second, third and fourth cases clearly refer to the type of the expression, which would not include any polymorphism considerations.
However, and I'm not entirely sure what "entity" means here, the first case appears to be naming the object refered to by the expression e. It is ambiguous to me as to whether "the type of the entity" means its runtime type, or its static type.
It's actually impossible to run into this problem, due to the restraints of that first case.
Consider:
struct A {};
struct B : A {};
int main()
{
A* x = new B();
// What is `decltype(*x)`?
}
The use of * makes us fall through to the third case.
And for references?
struct A {};
struct B : A {};
int main()
{
A& x = *(new B());
// What is `decltype(x)`?
}
x is a reference with type A&, and it is this "entity" whose type results.
The only way to use the first case is by directly naming an object, and we cannot do that in a way that hides a runtime type:
struct A {};
struct B : A { void foo() {} };
int main()
{
A x = B(); // well, you've sliced it now, innit?
decltype(x) y;
y.foo(); // error: ‘struct A’ has no member named ‘foo’
}
This is why, according to these answers, it is always the static type of an object that is used.
You don't have to look into the individual points: the results
of decltype are a type known to the compiler, which pretty
much excludes any dynamic typing. And the last line that you
quote couldn't be more explicit: the specifier is not evaluated,
which also excludes any dynamic typing.
It's basically a question of what "entity" means here (the possible meanings are defined in clause 3). Consider
struct A {
int a;
};
int main() {
A a = {};
const A b = {};
const A *aptr = (rand() % 42) ? &a : &b;
decltype(aptr->a) x = 0;
decltype((aptr->a)) y = 0;
}
Is x's type const int or int? If you take entity to mean "member", it is int because the member A::a has type int. If you take the entity kind "object", then the type is either const int or int, depending on the result of rand(). Objects, their existence and properties (including their type in general) is a runtime issue.
I say that this is not a real ambiguity. Because everyone knows what is meant and because the Standard uses the phrase "named by e" rather than "referred to by e" or "denoted by e" indicating that it is only the name lookup result that it is consulted.
Note that the type of y is always const int&, because the type of the expression aptr->a is const int and it is an lvalue.