Обход ограничений C++ для неконстантных ссылок на временные ссылки

У меня есть структура данных C++, которая является "блокнотом" для других вычислений. Он не долговечен и не часто используется, поэтому не критичен к производительности. Однако он включает в себя генератор случайных чисел среди других обновляемых полей отслеживания, и хотя фактическое значение генератора не важно, важно, чтобы значение обновлялось, а не копировалось и использовалось повторно. Это означает, что в общем случае объекты этого класса передаются по ссылке.

Если экземпляр нужен только один раз, наиболее естественным подходом является его создание везде, где это необходимо (возможно, с использованием фабричного метода или конструктора), а затем передача блокнота в метод потребления. Сигнатуры методов потребителей используют передачу по ссылке, поскольку они не знают, что это единственное использование, но фабричные методы и конструкторы возвращаются по значению - и вы не можете передавать безымянные временные ссылки по ссылке.

Есть ли способ избежать засорения кода неприятными временными переменными? Я хотел бы избежать таких вещей, как:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);

Я мог бы сделать блокнот по сути mutable и просто пометить все параметры const &, но это не кажется мне лучшей практикой, поскольку это вводит в заблуждение, и я не могу сделать это для классов, которые я не полностью контролирую. Передача по rvalue -ссылке потребует добавления перегрузок ко всем потребителям блокнота, что противоречит цели - иметь четкий и лаконичный код.

Учитывая тот факт, что производительность не является критичной (но размер кода и удобочитаемость), каков наилучший подход к передаче в такой блокнот? Использование функций C++0x приемлемо, если требуется, но предпочтительно должно быть только C++03.

Изменить: Чтобы было ясно, использование временного выполнимо, это просто неудачный беспорядок в коде, который я хотел бы избежать. Если вы никогда не называете временное имя, оно явно используется только один раз, и чем меньше строк кода для чтения, тем лучше. Кроме того, в инициализаторах конструкторов невозможно объявить временные значения.

5 ответов

Решение

Хотя нельзя передавать значения rvalue функциям, принимающим неконстантные ссылки, можно вызывать функции-члены для значений rvalue, но функция-член не знает, как она была вызвана. Если вы вернете ссылку на текущий объект, вы можете преобразовать rvalues ​​в lvalues:

class scratchpad_t
{
    // ...

public:

    scratchpad_t& self()
    {
        return *this;
    }
};

void foo(scratchpad_t& r)
{
}

int main()
{
    foo(scratchpad_t().self());
}

Обратите внимание, как звонок self() дает выражение lvalue, хотя scratchpad_t это значение.

Пожалуйста, исправьте меня, если я ошибаюсь, но параметры ссылок Rvalue не принимают ссылки lvalue, поэтому их использование потребует добавления перегрузок ко всем потребителям блокнота, что также вызывает сожаление.

Ну, вы могли бы использовать шаблоны...

template <typename Scratch> void foo(Scratch&& scratchpad)
{
    // ...
}

Если вы позвоните foo с параметром rvalue, Scratch будет выведено scratchpad_t, и поэтому Scratch&& будет scratchpad_t&&,

И если вы позвоните foo с параметром lvalue, Scratch будет выведено scratchpad_t&и из-за правил свертывания ссылок, Scratch&& также будет scratchpad_t&,

Обратите внимание, что формальный параметр scratchpad является именем и, следовательно, lvalue, независимо от того, является ли его тип ссылкой lvalue или ссылкой rvalue. Если вы хотите пройти scratchpad Что касается других функций, вам больше не нужен шаблонный трюк для этих функций, просто используйте параметр ссылки lvalue.

Кстати, вы понимаете, что временный блокнот участвует в xyz.initialize_computation(scratchpad_t(1, 2, 3)); будет уничтожен, как только initialize_computation сделано, верно? Хранение ссылки внутри xyz Объект для последующего пользователя будет крайне плохой идеей.

self() не должен быть методом-членом, это может быть шаблонная функция

Да, это также возможно, хотя я бы переименовал его, чтобы прояснить намерение:

template <typename T>
T& as_lvalue(T&& x)
{
    return x;
}

Проблема только в том, что это:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);

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

auto useless_temp = factory(rng_parm);

Лично я бы предпочел увидеть const_cast чем mutable, Когда я вижу mutableЯ предполагаю, что кто-то делает логично constи не думай об этом. const_cast однако поднимает красные флажки, как и код, как это должно.

Один вариант будет использовать что-то вроде shared_ptr (auto_ptr будет работать тоже в зависимости от того, что factory и передает его по значению, что позволяет избежать затрат на копирование и поддерживать только один экземпляр, но может быть передано из фабричного метода.

Если вы выделите объект в куче, вы сможете преобразовать код во что-то вроде:

std::auto_ptr<scratch_t> create_scratch();

foo( *create_scratch() );

Фабрика создает и возвращает auto_ptr вместо объекта в стеке. Возвращенный auto_ptr Временный будет владеть объектом, но вы можете вызывать неконстантные методы для временного и вы можете разыменовать указатель, чтобы получить реальную ссылку. В следующей точке последовательности смарт-указатель будет уничтожен, а память освобождена. Если вам нужно пройти то же самое scratch_t к различным функциям подряд вы можете просто захватить умный указатель:

std::auto_ptr<scratch_t> s( create_scratch() );
foo( *s );
bar( *s );

Это можно заменить на std::unique_ptr в следующем стандарте.

Я отметил ответ FredOverflow как ответ на его предложение использовать метод для простого возврата неконстантной ссылки; это работает в C++03. Для этого решения требуется метод-член для каждого типа, похожего на блокнот, но в C++0x мы также можем написать этот метод более широко для любого типа:

template <typename T> T & temp(T && temporary_value) {return temporary_value;}

Эта функция просто пересылает нормальные ссылки на lvalue и преобразует ссылки на rvalue в ссылки на lvalue. Конечно, выполнение этого возвращает изменяемое значение, результат которого игнорируется - что в точности совпадает с тем, что я хочу, но может показаться странным в некоторых контекстах.

Другие вопросы по тегам