13

At 3.10/10, the standard says:

An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]

So, rvalues are non-modifiable except under certain circumstances. We're told that calling a member function is one of those exceptions. This gives the idea that there are ways of modifying objects other than calling a member function. I can't think of a way.

How can one modify an object without calling a member function?

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510

7 Answers7

3

How can one modify an object [that's specified by an rvalue expression] without calling a member function?

I know of only one way to do that, namely to bind the object to a reference to const, and then cast away the const-ness.

E.g.

template< class Type >
Type& tempRef( Type const& o ) { return const_cast< Type& >( o ); }

struct S { int x; };

int main()
{ tempRef( S() ).x = 3; }

This is because a temporary object is not const itself unless it is of const type, so the example above does not cast away original const-ness (which would be UB).

EDIT, added: Luc Danton’s answer showed another (non-general) way, namely where the temporary's construction stores some reference or pointer to the object in some accessible location.

Cheers & hth.,

Community
  • 1
  • 1
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • "This is because a temporary object is not const itself unless it is of const type", that's only guaranteed to be the case in C++0x though - should be mentioned. – Johannes Schaub - litb Jun 24 '11 at 18:52
2

This seems to be accepted:

struct T {
   int x;
};

int main() {
   T().x = 3;
}

I am slightly surprised that this works, because IIRC the LHS of op= must be an lvalue, yet the following implies that even T().x is an rvalue:

struct T {
   int x;
};

void f(int& x) {
   x = 3;
}

int main() {
   f(T().x);
}

Edit: As of 4.6, GCC does warn about T().x = 3: error: using temporary as lvalue.

I can't think of any other way to modify a class object other than through data member access or member function calls. So, I'm going to say... you can't.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • g++ says `error: using temporary as lvalue [-fpermissive]` on the line `T().x = 3;`. – fredoverflow Jun 24 '11 at 10:49
  • 1
    @Fred: what flags are you using? `g++ -Wall -Wextra -pedantic` gives me no output on the first example. – R. Martinho Fernandes Jun 24 '11 at 10:53
  • It is additionally possible to modify objects of appropriate types through e.g. `std::memset` but since this needs a pointer it cannot be applied to rvalues either. – Luc Danton Jun 24 '11 at 11:05
  • @Martinho: None, I just say `g++ foobar.cpp`. I use g++ 4.6.0 – fredoverflow Jun 24 '11 at 11:07
  • @Fred: confirmed. 4.6 does indeed warn. For the record 4.5 doesn't. – R. Martinho Fernandes Jun 24 '11 at 11:09
  • @Thomalak: it's no big mystery. The first example invokes the automatically generated copy assignment operator, which is a member function. The second example doesn't. But you are on to a decidedly silly effect of the rules, namely that you can assign to the complete class type rvalue result of a function call (this uses the assignment operator), but not to individual fundamental type parts of it. Heh. :-) – Cheers and hth. - Alf Jun 24 '11 at 11:16
  • 3
    @Alf: That famous copy assignment operator for `int`. – Lightness Races in Orbit Jun 24 '11 at 11:18
  • @Thomalak: oh, sorry, I didn't see that (since you wrote that it seems to be accepted). That's a buggy compiler. Try it with [Comeau Online](http://www.comeaucomputing.com/tryitout/), it will give you correct error message. – Cheers and hth. - Alf Jun 24 '11 at 11:20
  • GCC 4.6 has rvalue references, so it doesn't conform to C++03 but C++0x which has different rules. However, it's worth noticing that the fact that you can only bind rvalues to const lvalue references is irrelevant of their constness. – Puppy Jun 24 '11 at 11:52
  • 2
    @DeadMG: Not by default. You need --std=c++0x for that. – R. Martinho Fernandes Jun 24 '11 at 12:18
  • @Fred, @Martinho: does gcc 4.6 (no access to it yet) warn if `x` is a custom class instead of a plain `int` ? – Matthieu M. Jun 24 '11 at 13:16
  • @Matthieu: btw my comment above said "warn" but it was a mistake on my part. It's an *error*. interesting: given `struct U{int y;}; struct T{U x;};` I get the following: `T().x = U()` produces no diagnostic, while `T().x.y = 3` produces the error. – R. Martinho Fernandes Jun 24 '11 at 15:36
  • @Martinho: I ought to have said "diagnose" rather than "warn" (the snippet clearly says "error" anyway) but it doesn't matter hugely. – Lightness Races in Orbit Jun 24 '11 at 15:47
  • @Martinho: It makes sense: `T().x = U()` uses compiler-generated `operator=(const U&)`, which _is_ member function. – Vitus Jun 24 '11 at 16:34
  • @Vitus: Ah! dumb me, I didn't realized :/ – Matthieu M. Jun 24 '11 at 18:03
  • `T().x` is an *rvalue* of *non-class* type. It is not legal to say `T().x = ...;`. It's also not an example of modifying an *rvalue* of *class* type :) – Johannes Schaub - litb Jun 24 '11 at 18:49
1

Modifying a temporary and not through an lvalue to that temporary:

#include <cstring>

class standard_layout {
    standard_layout();
    int stuff;
};

standard_layout* global;

standard_layout::standard_layout()
{ global = this; }

void
modify(int)
{
    std::memset(global, 0, sizeof *global);
}

int
main()
{
    modify( (standard_layout {}, 0) );
}

I don't think it's correct to assume that rvalues of class types are non-modifiable. I now understand that paragraph as 'for non-class types, an lvalue for an object is needed in order to modify that object'.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • Yet that's what the standard says in 3.10/10. Aren't you using an lvalue here? Or is it pertinent that the object was _originally_ seen as an rvalue? Argh! – Lightness Races in Orbit Jun 24 '11 at 11:58
  • @Tomalak `global` as it appears in the call to memset is an lvalue, but not an lvalue *for the object* being modified (that would be `*global`), as appears in the paragraph. – Luc Danton Jun 24 '11 at 12:01
  • @Luc: Hmm ok. Presumably, though, at some point `memset` is using `*global`. – Lightness Races in Orbit Jun 24 '11 at 12:02
  • @Tomalak I believe a `memset` that uses char pointers would be conforming (and, well, plain obvious) and `*(char*)this` is not an lvalue for `*this`. – Luc Danton Jun 24 '11 at 12:04
  • @Luc: No lvalue-to-rvalue conversion for "use"? – Lightness Races in Orbit Jun 24 '11 at 12:40
  • @Luc: Ah, for the pointer copy but not for the actual eventual pointee modification. OK, nice! I'm glad that this means I don't have to change [my answer here](http://stackoverflow.com/questions/6466253/if-temporaries-are-implicitly-non-modifiable-how-does-this-work/6466316#6466316) where I stole your code. – Lightness Races in Orbit Jun 24 '11 at 12:43
0

I can think of one way:

If your class exposes public member variables, you can assign directly to those member variables. For example:

class A
{
    public:
        int _my_var;
...
};

int main(int argc, char** argv)
{
    A *a = new C();
    a->_my_var = 10;
}

This is not a good programming style though - exposing a member variable as public isn't something I would advocate or even suggest.

Also, if you can do something really weird, such as directly writing some address in memory, an offset from the pointer to the class object - but why would you do that?

Aleks G
  • 56,435
  • 29
  • 168
  • 265
0

How can one modify an object without calling a member function?

By assigning a value to one of the object's visible data members, of course.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
0

Doing an implicit cast is sort of like calling a member function -- also modifying rvalue refs seems to work.

Tested the following in vc++10 and g++ 4.4.

struct b { int i; b(int x) : i(x) {} };
struct a { int i; a() : i(0) { } operator b() { return i++ /* this works */, b(i); } };
a f(a&& x) { return x.i++ /* this works */, x; }
int main() { b b = f(a()); /* implicit operator b() cast; b.i will equal 2 */ }
Nick
  • 5,765
  • 5
  • 27
  • 36
-1

A member function can change the member directly, but it can also delegate that responsibility:

struct Foo {
  int x;
  void Bar() { scanf("%d", &x); }
};

The current wording of the standard has the advantage that one doesn't need to argue whether this is a case of Bar changing the object. If we'd agree that scanf changes the object, then that's just another example.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Huh, you're *calling a member function*. And we don't need to argue whether this is a case of `Bar` changing the object **because it does**, not because of any wording in the standard. You're taking a *non-const pointer* to a member. – R. Martinho Fernandes Jun 27 '11 at 02:49