5

I know that references can extend the lifetime of a return value in C++. With this phylosophy, I tried the following: I have three clases, 'tensor', 'view' and 'mutable_view'. Operator () on a tensor returns a "const view" object. This view has a private copy constructor so that the view cannot be copied, as it keeps information about the tensor that might not survive beyond the current statement.

#include <iostream>
#include <algorithm>

struct tensor {

  int data[10];

  class view {
    const int *const data;

    view();
    view(const view &);
  public:
    view(const int *new_data) : data(new_data) {}
    int operator*() const { return *data; }
  };

  class mutable_view {
    int *const data;

    mutable_view();
    mutable_view(const mutable_view &);
  public:
    mutable_view(int *new_data) : data(new_data) {}

    void operator=(const view &v) {
      *data = *v;
    }
  };

  tensor(int n) {
    std::fill(data, data+10, n);
  }

  const view operator()(int ndx) const {
    return view(data + ndx);
  }

  mutable_view at(int ndx) {
    return mutable_view(data + ndx);
  }
};


int main()
{

  tensor a(1);
  tensor b(2);

  b.at(2) = a(2);

  for (int i = 0; i < 10; i++)
    std::cout << "a[i] = " << b.data[i] << std::endl;

  for (int i = 0; i < 10; i++)
    std::cout << "b[i] = " << b.data[i] << std::endl;

  exit(0);
}

The problem is that, while this code works in gcc (depends on the version), icc signals a warning and open64 simply does not build it: it demands that the constructors from 'view' be public. Reading icc's message the idea seems to be that the right hand value could be potentially copied by the compiler and thus constructors are needed.

Is this really true? Is there a workaround that preserves the syntax I want to build? By the way they are built, and in order to avoid inefficient implementations based on shared_ptr or other stuff, I need to keep the 'view' objects un-copiable.

Edit 1:

tensor cannot control the lifetime of the views. The views are created by the accessors and their lifetime is limited to the statement where they are used, for the following reasons:

  • These views are only used for two things: (i) copying data, (ii) extracting portions of the tensor.
  • The tensors are multidimensional arrays that implement copy-on-write semantics, which means that the views cannot be long-lived objects: if the data changes, they expire.

Edit 2:

Changed the pseudocode description (guys, if you see '...' do you expect it to be compilable?) with one that builds on 'icc' and does not on clang/open64

Community
  • 1
  • 1
Juanjo
  • 659
  • 3
  • 9
  • Could you post an SSCCE (see sscce.org). I'm surprised gcc compiles this. – juanchopanza Feb 04 '14 at 22:50
  • `const view operator(int idx)`? it seems not to be operator overloading.. `operator` is keyword. – ikh Feb 05 '14 at 02:01
  • Do you want to say `operator ()(int idx)`? – ikh Feb 05 '14 at 02:02
  • Of course this is _not_ the original code. I did not write a SSCCE but an idea of what this looks like. – Juanjo Feb 05 '14 at 11:33
  • Dont return by const value. It only serves to disable certain copy elision optimizations – Manu343726 Feb 05 '14 at 12:15
  • Manu343726, changing 'const view' to 'view' in the tensor::operator() does not change anything in this respect. Latest clang/gcc complains about the visibility of the copy constructor. – Juanjo Feb 05 '14 at 13:18

2 Answers2

2

Go ahead and let the default copy constructors be public. And document that a view or mutable_view is "invalidated" when its tensor is changed or destroyed.

This parallels how the Standard Library deals with iterators, pointers, and references that have a lifetime which depends on another object.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • yeah, that seems to be the only option :-/ It's a pity, I expected that C++'s powerful syntax would help in detecting this kind of errors. – Juanjo Feb 05 '14 at 20:44
0

As others already pointed out you missed () here:

const view operator(int ndx) const;

Anyway this declaration means that return value is copied. If you want to avoid copying just return reference for an object:

const view& operator()(int ndx) const;

As I understand 'tensor' is container of 'views' so it manages there lifetime and its safe to return reference. For the same reason tensor::at should return reference to mutable_view:

mutable_view& at(int ndx);

Another question is about default constructor of 'view' - it looks like 'tensor' has to be a friend of 'view' to be able to create its instances

By the way - prefer using 'size_t' as index type instead of just 'int'

My overall feeling of this code - you are trying to implement kind of domain language. Maybe it's better to focus on concrete calculation task?

AlexT
  • 1,413
  • 11
  • 11
  • 1
    The only `view` and `mutable_view` objects are temporary, so cannot be returned by reference. – aschepler Feb 05 '14 at 13:18
  • I do not understand what you mean by 'concrete calculation task', but this is a library I am developing which works as a replacement for Matlab (https://github.com/juanjosegarciaripoll/tensor) In this respect, we need to be able to copy sections of arrays into other arrays and for the target audience (Physicists with little or no knowledge of programming) I do need the operator= and operator() syntax and copy-on-write semantics. – Juanjo Feb 05 '14 at 13:20