Как продлить время жизни временного выражения в диапазоне?

Я получаю свисающие ссылки при использовании дальнего цикла. Рассмотрим следующее выражение C ++ 14 (полный пример программы ниже):

    for(auto& wheel: Bike().wheels_reference())
wheel.inflate();

Это вывод:

 Wheel()
Wheel()
Bike()
~Bike() with 0 inflated wheels.
~Wheel()
~Wheel()
Wheel::inflate()
Wheel::inflate()

Очевидно, что-то идет не так. Доступ к колесам по истечении срока их службы равен 0, а не ожидаемому 2.

Простое решение — ввести переменную для Bike в main, Тем не менее, я не контролирую код в main или же Wheel, Я могу только изменить структуру Bike,

Есть ли способ исправить этот пример, только изменив Bike?

Успешное решение может либо выйти из строя во время компиляции, либо подсчитать 2 накачанные шины и не коснуться каких-либо объектов по истечении срока их службы.

#include <cstdlib>
#include <iostream>
#include <array>
#include <algorithm>
using std::cout;
using std::endl;

struct Wheel
{
Wheel() { cout << " Wheel()" << endl; }
~Wheel() { cout << "~Wheel()" << endl; }
void inflate() { inflated = true; cout << " Wheel::inflate()" << endl; }
bool inflated = false;
};

struct Bike
{
Bike() { cout << " Bike()" << endl; }
~Bike() {
cout << "~Bike() with " << std::count_if(wheels.begin(),    wheels.end(),
[](auto& w) { return w.inflated; }) << " inflated wheels." << endl;
}
std::array<Wheel, 2>& wheels_reference() { return wheels; }
std::array<Wheel, 2> wheels{Wheel(), Wheel()};
};

int main()
{
for(auto& wheel: Bike().wheels_reference())
wheel.inflate();
return EXIT_SUCCESS;
}

6

Решение

Удалить перегрузку rvalue wheels_reference,

std::array<Wheel, 2>& wheels_reference() & { return wheels; }
std::array<Wheel, 2>& wheels_reference() && = delete;

Таким образом, вы не будете возвращать ссылку на члена временного.

Ваш пример использования Bike учебный класс

for(auto& wheel: Bike().wheels_reference())
wheel.inflate();

Затем откажется скомпилировать (вывод clang 3.4):

test.cpp:31:29: error: call to deleted member function 'wheels_reference'
for(auto& wheel: Bike().wheels_reference())
~~~~~~~^~~~~~~~~~~~~~~~
test.cpp:24:27: note: candidate function has been explicitly deleted
std::array<Wheel, 2>& wheels_reference() && = delete;
^
test.cpp:23:27: note: candidate function not viable: no known conversion from 'Bike' to 'Bike' for object argument
std::array<Wheel, 2>& wheels_reference() & { return wheels; }

Если срок службы временного продлен вручную, все работает.

Bike&& bike = Bike();
for(auto& wheel: bike.wheels_reference())
wheel.inflate();
6

Другие решения

Лучшее решение — прекратить получать члены типа через вызов функции-члена на временной основе.

Если wheels_reference если бы функция не была членом, вы могли бы просто объявить ее так:

wheels_reference(Bike &bike);

Поскольку неконстантный параметр lvalue не может быть присоединен к временному, вы не сможете вызвать wheels_reference(Bike()), поскольку wheels_reference является функцией-членом, вам просто нужно использовать синтаксис функции-члена, чтобы сказать то же самое:

std::array<Wheel, 2>& wheels_reference() & //<--
{ return wheels; }

Если пользователь сейчас пытается позвонить Bike().wheels_reference()Компилятор пожалуется.

3

Следующая ужасная штуковина, кажется, удовлетворяет всем условиям:

#include <memory>

struct Bike
{
// Bike() { cout << " FakeBike()" << endl; }
// ~Bike() { cout << "~FakeBike()" << endl; }
struct RealBike;
struct Wrap {
std::shared_ptr<RealBike> parent;
auto begin() { return parent->wheels.begin(); }
auto end() { return parent->wheels.end(); }
};
struct RealBike {
RealBike() { cout << " Bike()" << endl; }
~RealBike() {
cout << "~Bike() with " << std::count_if(wheels.begin(),    wheels.end(),
[](auto& w) { return w.inflated; }) << " inflated wheels." << endl;
}
std::array<Wheel, 2> wheels;
};
std::shared_ptr<RealBike> real = std::make_shared<RealBike>();
Wrap wheels_reference() { return Wrap{real}; }
};

Что мне не нравится, так это то, что он требует обертывания всего API std::array<Wheel, 2> в Wrap,

1

Вы можете просто добавить несколько cv-ref-квалифицированных перегрузок wheels_reference функция-член:

std::array<Wheel, 2>& wheels_reference() & { return wheels; }

std::array<Wheel, 2> const & wheels_reference() const & { return wheels; }

std::array<Wheel, 2> wheels_reference() && { return std::move(wheels); }

std::array<Wheel, 2> wheels_reference() const && { return wheels; }

Обратите внимание, если объект является временным (&& case), то вы должны вернуть значение (созданное с помощью копирования из элемента данных или, что еще лучше, сконструированное с помощью Move) или ссылку.

Имея все четыре перегрузки, вы покрываете все возможные варианты использования.

1
По вопросам рекламы [email protected]