4

I have a class K and I am constructing an object in a call to function test. So I believe that the constructed K is called an r-value. (Is that true?)

But I am puzzled, and bothered that the K object is apparently const, not mutable. I don't want it to be.

Simple test program:

#include <iostream>

class K {};

std::string test (K &k) {return "mutable";}

std::string test (const K &k) {return "const";}

int main (int argc, const char **argv) {
  std::cerr << "K constructed for function argument is " << test(K{}) << "\n";
  K k;
  std::cerr << "K constructed for local variable is " << test(k) << "\n";
}

Output:

K constructed for function argument is const

K constructed for local variable is mutable

Note that when I create the K on the fly for passing as function argument I get a const object, whereas when I create it as a local variable I get a mutable.

For my purposes I really want a mutable for both cases. Can you tell me how to do that, or else convince me why I shouldn't?

Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97
  • 4
    What the point of be able to *mutate* an object you are going to lose just after your function? The fact you want to do this probably mean you have a bad design either in your function or in your class. – Holt Nov 06 '15 at 16:30
  • You cannot bind an rvalue to a non-const reference. Some compilers offer this as an extension but I would stay well away from it. – Mohamad Elghawi Nov 06 '15 at 16:31
  • I thought you might ask that! Without going into a lot of detail, here is one reason, which I believe is close to my use case: Function test() calls some method m() of K that is non-const and uses the result to perform a side effect on objects that are not part of K. There may be legitimate reasons that m() was not const and the effects it has on the temporaray K are real, though irrelevant since K is going away. – David Cogen Nov 06 '15 at 16:33
  • @Holt, it is usually not that you want to mutate it, but that you call functions on it which are non-const. – SergeyA Nov 06 '15 at 16:34
  • @SergeyA which is saying the same thing, otherwise the functions would be const – BeyelerStudios Nov 06 '15 at 16:34
  • @BeyelerStudios not neccessarily. They might be non-const because someone forgot to mark them cost, or the class might be designed in such a way that functions accept or return values through members of the class (sometimes this neccessary evil). – SergeyA Nov 06 '15 at 16:36

3 Answers3

3

A reference cannot bind to a temporary object but a const T& can. Since K{} is a temporary std::string test (const K &k) {return "const";} is chosen.

If you really want to be able to modify the temporary then you can use and r-value reference

std::string test (K &&k) {return "r-value";}

Live Example

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

In standard C++, you can not bind temporary object to non-const reference. You can bind it to rvalue references, though. Users of MSVC enjoy the 'extension' of binding temporaries to non-const references.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
0

It looks like you got your syntax mixed up between l-value references and r-value references.

K& k : this is a non-const l-value reference. It can usually only be bound to variables that have names. This syntax implies (typically) you are taking in k and modifying inside your function, such that k is also the output of the function. Doing this with a temporary doesn't actually make sense, because the temporary will be gone. It only really makes sense to do for variables (esoteric compiler extensions aside).

const K& k : const reference. This can bind to l or r-values. This means you are using k within your function, but (generally) don't need to copy it, and certainly won't modify it. It's safe-enough, because you can't modify the temporary (so there's no implicit assumption that it will be used later), and the temporary will exist for the life of the function.

K&& k : This is an r-value reference. It implies move semantics, namely that the function using k will only accept a temporary value (or one made movable with std::move), and it will take ownership of said temporary.

In practice, the only place I've personally found necessary (or really even appropriate) to use r-value references is:

  1. class constructors for containers. These should have a move constructor/move assigner, so that they can take ownership when created with temporary values.
  2. forwarding functions. I.e. wrapper functions which only call lower-level class functions. If those low-level class functions support r-values, a universal reference can be used to make sure that support doesn't get messed up.

Your first instinct should probably be to avoid r-values in function calls.

On the other hand, if you were actually trying to bind the temporary to a non-const reference I think this question/answer has a better explanation as to why it's not allowed: How come a non-const reference cannot bind to a temporary object?

Community
  • 1
  • 1
Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97