2

Temporary objects are sometimes created in C++. One example from the STL valarray class would be the slice_array, which is invoked every time a series of indices of a valarray is selected. http://www.cplusplus.com/reference/valarray/slice_array/

My question is this:

When passing these temporary objects as arguments to a function, is a copy of these objects passed, or only a reference?

E.g. imagine these two naive functions:

double simple_product(double* inp,int length){
  double res=1;
  for(int i=0;i<length;++i){
      res = res*inp[i];
  }
  return(res);
}

double sum_selected(valarray<double> v){

    simple_product(&v[0],v.size());
    return(v.sum());
}

If I call them in the following fashion:

valarray<double> valery(10,10);
size_t sel[] = {1,3,4};
valarray<size_t> selection (sel,3);
cout << sum_selected(valery[selection]);

will a new object with a size of 3*size_t be temporarily created within the stack of the function sum_selected or not?

Please note that declaring the function as: double sum_selected(valarray<double> & v) is not permitted (temporary objects can only be bound to const references).

The reason why this is interesting is that, for example here, it is not possible to declare the function as:

double sum_selected(const valarray<double> & v), because then the function simple_product (which is to be assumed unalterable) cannot be called. However, making a temporary copy of the passed argument would be problematic for memory in case of big arrays.

Ataxias
  • 1,085
  • 13
  • 23
  • Compile your code with optimizations turned on. In addition, generate the assembly listing and see what is happening. – PaulMcKenzie Sep 29 '14 at 17:21
  • Why don't you change your parameter to `const double * inp` and then you make the other parameter `const valarray& v` – Neil Kirk Sep 29 '14 at 17:23
  • Is simple_product really unalterable? As, in my opinion, it is wrong. If it cannot be changed, this would be an acceptable use of `const_cast` when you pass a const valarray to the function, which will not modify it but does not take it as const for some reason. – Neil Kirk Sep 29 '14 at 17:25
  • @NeilKirk: That would be a band-aid which I expect to work, though if ever a really constant object is passed, that will be UB. – Deduplicator Sep 29 '14 at 17:35
  • @Deduplicator It is not UB as the function simple_product doesn't modify it. It is a band-aid, but he states the function cannot be changed. – Neil Kirk Sep 29 '14 at 17:37
  • @NeilKirk: I think I saw a quite convincing argument that casting away the const of an actually constant object is already UB. Cannot quite recall when and where though. – Deduplicator Sep 29 '14 at 17:39
  • @Deduplicator It is only UB if you cast away the const of a const object and modify it. Otherwise what is the point of const_cast instead of reinterpret_cast? It is to allow interaction with legacy code that doesn't use const parameters. – Neil Kirk Sep 29 '14 at 17:42
  • @NeilKirk: Is that so? What about e.g. writing a search-function for your collection, returning by const reference if the collection is const, otherwise modifiable. non-const delegates the work to const and const-casts the result... – Deduplicator Sep 29 '14 at 17:45
  • @Deduplicator Well that is more of a modern innovation.. – Neil Kirk Sep 29 '14 at 17:46
  • Thank you for your input. Modifying **simple_product** would of course be a logical workaround, however: 1) this and a number of other similarly defined functions could be (and indeed in my case are) frequently recurring in the code and that would require making wrapper functions for all of them, and 2) the question was mainly of an academic interest in how the language works, rather than a practical troubleshooting question. – Ataxias Sep 30 '14 at 08:40

2 Answers2

4

If the function is declared to take its argument by value, then it's passed by value, creating a new copy of the object:

void f(thing t);           // pass by value

If it's declared to take its argument by reference, then it's passed by reference. But it can only take a temporary by const or rvalue reference:

void f(thing const & t);   // OK: const lvalue reference
void f(thing && t);        // OK: rvalue reference
void f(thing & t);         // Error: lvalue reference can't bind to temporary

Passing by reference doesn't create a new object, and the lifetime of the temporary is such that it's valid during the function call, until the end of the statement that creates it.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Arguments that are passed by value are not always copied. –  Sep 29 '14 at 17:22
  • @rightfold: Indeed, the full story is rather more complicated, and copying from a temporary to initialise a parameter of the same type may be elided. I'll keep the answer simple, and leave this for anyone who wants to know the gory details of copy semantics: http://stackoverflow.com/questions/12953127 – Mike Seymour Sep 29 '14 at 17:29
  • What is worse is that the above case has a `thing` that is of a different type than the argument passed to it. The `thing` is not copied in the OP's question, regardless of elision, but rather constructed. – Yakk - Adam Nevraumont Sep 29 '14 at 18:20
  • @Mike Thank you, I was unaware of the existence of rvalue references! – Ataxias Sep 30 '14 at 08:34
1

valarray<T>::operator[](valarray<size_t> const&) returns an indirect_array<T> (reference), or a valarray<T> for the const-qualified operator, not a slice_array.

If you want to be able to access the selected elements as a contiguous array, then you'll need to collect them into a contiguous array. The converting constructor valarray<T>::valarray(indirect_array<T> const&) does this for you. Reference semantics would be useless in this case, as there is no existing object that has your desired elements arranged contiguously.

Changing the function signature to double sum_selected(valarray<double> const&) would make no difference to your code, as a temporary valarray<double> is constructed anyway. It would be more efficient in the case where valery is passed directly, without subscripting.

ecatmur
  • 152,476
  • 27
  • 293
  • 366