0

So I'm implementing a native arrays wrapper which will allow such to be passed as function arguments and to be returned. I'm having a trouble however with casting it to a native array as native arrays can't be returned. As an replacement I decided to use 'rvalue' reference return type of the casting operator but this will not act correctly because if I want to bind the returned object into an 'rvalue' reference in order to extend it's life-time this won't happen as it's an 'xvalue' and not 'prvalue'. Is there any solution for the problem? Maybe some 'prvalue' cast? Or if there is some other way to implement this implicit cast to 'array'?

The class:

template<typename type>
struct tmp
{
    tmp() {}
    tmp(const tmp &) = default;
    tmp(const type & arg) : tmp(*(const tmp*)arg) {}

    && operator type() && {return static_cast<type&&>(d);}

    ~tmp () { cout << "tmp destructor" << endl; }

    type d;

};

And the code that uses it:

tmp<tmp<int [4]>> Func() // used 'tmp<int [4]>' instead of array to track object destruction (but normally it should be an native array type
{
    return tmp<tmp<int [4]>>();
}

int main()
{
    tmp<int [4]> &&tmp1 = Func(); //implicit cast (from 'tmp<tmp<int [4]>>') to 'tmp<int [4]>', calls tmp::operator type()

    cout << "Here" << endl;

    return 0;
}

The program output:

tmp destructor

tmp destructor

Here

As you see the return value of the cast operator is not extended.

Life example.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66

1 Answers1

2

A prvalue is an rvalue that is not an xvalue, aka "a temporary object or subobject thereof, or a value that is not associated with an object."

You cannot create an array that is a a temporary object (12.2), nor can you create an array value that is not associated with an object.

For an array to be a prvalue, that leaves a subobject thereof of a temporary object.

So a tmp:

template<typename type>
struct tmp
{
  tmp() {}
  tmp(const tmp &) = default;
  tmp(tmp &&) = default;
  tmp(const tmp &&o):tmp(o) {}
  tmp(tmp &o):tmp(const_cast<tmp const&>(o)){}

  template<class... Ts>
  tmp(Ts&&...ts) : v{std::forward<Ts>(ts)...} {}

  ~tmp () { std::cout << "tmp destructor\n"; }

  type v;
};

A wrap_as_tmp:

template<class X, class... Ts>
tmp<X> wrap_as_tmp(Ts&&... ts)
{
  return {std::forward<Ts>(ts)...};
}

to track destruction we use noisy:

struct noisy {
  ~noisy() { std::cout << "bang\n"; }
};

Then test:

int main() {
  auto&& x = wrap_as_tmp<noisy[4]>().v;
  std::cout << "There\n";
}

and note that There outputs before the noisy objects explode.

live example

Note the use of .v at the end of the function call.

If your goal is to avoid that, too bad, you cannot.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524