0

I am struggling with miniproject, where I have to do this

const Foo &f = func(arr(1.3, 2.4, 3.8, 4.3));

Foo is non-duplicable class containing single float value as memeber, arr is simple function that returns std::vector of Foo objects initialized with values passed as parameter into arr. Finally func is defined as following:

const Foo &func(const std::vector<Foo> &in)
{
    return *std::max_element(in.begin(), in.end());
}

Foo has defined operator< function, that determines which of the two Foos have bigger value, thus std::max_element works as should. Foo has also defined Foo(Foo &&v) that copies value.

I know that parameter of func is created as temporary.

Application is compilable, but after debugging, the object f contains some random value, that is not even in passed array (since the object was already destroyed?).

Is there any possible way to change func so this will work?

Brykyz
  • 587
  • 4
  • 30
  • 1
    The result will be a dangling reference. That's bad. The way around it is to not pass in a temporary. Or don't return a reference. – Eljay Mar 26 '21 at 17:56
  • Is there any possible way to change `func` so this will work? I cannot change line where the `func` is called and result assigned. – Brykyz Mar 26 '21 at 17:58
  • 2
    `Foo func(...)` would work. Return a value. – Drew Dormann Mar 26 '21 at 17:58
  • 1
    Return by value. `const Foo& f = ` will extend the lifetime of the returned temporary until the end of the enclosing scope. – alter_igel Mar 26 '21 at 17:59
  • Related: https://stackoverflow.com/questions/39718268/why-do-const-references-extend-the-lifetime-of-rvalues – alter_igel Mar 26 '21 at 18:00
  • `Foo func(std::vector const& v) { auto result = std::max_element(v.begin(), v.end()); if (result == v.end()) throw "oopsies"; return *result; }` – Eljay Mar 26 '21 at 18:00
  • Function code is valid, since reference to element of container passed to argument is returned. The only case when it can fail is when container is empty. Problem is that to argument temporary object is passed, so basically this line is wrong: `const Foo &f = func(arr(1.3, 2.4, 3.8, 4.3));` – Marek R Mar 26 '21 at 18:06
  • @Eljay I think this wouldn't work because of deleted copy constructor of `Foo`. – Brykyz Mar 26 '21 at 18:11
  • Is your type movable? If so, it should be possible to return by value by moving the variable out from the vector. – alagner Mar 26 '21 at 18:12
  • @alterigel this is not this case. If `func` would return a value then linked topic kicks in. Here is Undefined behavior. – Marek R Mar 26 '21 at 18:13
  • 1
    @Brykyz how about move? Move constructor could do the job here. – Marek R Mar 26 '21 at 18:15
  • @MarekR I have never worked with move constructor, I was reading documentation. Anyway, it did the job! Thank you for your help! – Brykyz Mar 26 '21 at 18:33

2 Answers2

2

Since the result of func is a reference to an element of the vector passed to it, then you must ensure that the vector argument is still alive when you access the result. Passing a temporary object (arr(1.3, 2.4, 3.8, 4.3)) means that vector will be destroyed as soon as func is evaluated and thus the reference returned is no good. Call func without a temporary vector parameter and you should be fine:

auto func_arg = arr(1.3, 2.4, 3.8, 4.3);
const Foo &f = func(func_arg);

There's nothing you can change within func to make the returned reference valid. The input must be a non-temporary.

Woodford
  • 3,746
  • 1
  • 15
  • 29
1

If func were to return by value, it would return a temporary object whose lifetime would be extended to that of f.

However, the object needs to be moved from the input array since it cannot be copied. This is a problem because the input parameter is a const reference. So provide an overload that takes a movable parameter that can bind to a temporary vector. This could be either receiving by value or taking a non-const r-value reference.

Foo func(std::vector<Foo> && in)
// ^     ^----------------^^------- non-const r-vlaue reference
// |------------------------------- return a temporary, not a reference
{
    return std::move(*std::max_element(in.begin(), in.end()));
    //     ^^^^^^^^^--------------------- move the result
}
JaMiT
  • 14,422
  • 4
  • 15
  • 31