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

StackOverflow https://stackoverflow.com/questions/3731089

Вопрос

У меня есть структура данных 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.

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

Это было полезно?

Решение

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

class scratchpad_t
{
    // ...

public:

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

void foo(scratchpad_t& r)
{
}

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

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

Пожалуйста, поправьте меня, если я ошибаюсь, но ссылочные параметры 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 как ответ на его предложение использовать метод для простого возврата неконстантной ссылки;это работает в С++ 03.Это решение требует метода-члена для каждого типа, подобного блокноту, но в C++0x мы также можем написать этот метод в более общем смысле для любого типа:

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top