Шаблоны выражений и ранжирование на основе в C++11
Насколько я понимаю, шаблоны выражений будут разбиты на ранжированные в C++11, так как for (auto x : expr)
имеет неявное auto&& __range = expr
в этом, и это приведет к висящим ссылкам.
Есть ли способ создать классы шаблонов выражений так, чтобы они либо правильно вели себя в зависимости от ранжирования, либо, по крайней мере, вызывали ошибку компиляции?
По сути, я бы хотел предотвратить вероятность того, что шаблоны выражений будут корректно компилироваться, но не будут работать во время выполнения из-за висячих ссылок. Я не возражаю против того, чтобы оборачивать шаблоны выражений во что-то перед использованием их в ранжированной основе, до тех пор, пока не будет никаких тихих ошибок времени выполнения, если пользователь забудет обернуть шаблоны выражений.
2 ответа
Я могу придумать несколько вариантов, каждый со своим уродством.
Одним из очевидных вариантов является использование указателей (вероятно, unique_ptr
) вместо ссылок. Конечно, чтобы это работало, требуется либо выделение из кучи, либо пользовательские распределители. Я думаю, что с хорошим распределителем у этого подхода есть свои достоинства. С другой стороны, перегрузка оператора будет просто неприятной.
Другой подход - хранить подвыражения по значению, а не по константной ссылке. Эффективность этого подхода очень зависит от компилятора, но, поскольку вы в основном имеете дело с кучей временных, я бы предположил, что современные компиляторы могут оптимизировать копии (или, по крайней мере, много копий).
Последний подход позволяет сохранить ту же структуру в вашем коде, но заставляет пользователя оценивать выражение. Это требует, чтобы у вас был только один итеративный тип, который является базовым типом выражения (скажем, std::vector<int>
). Ни один из классов выражений не должен иметь begin
а также end
методы или функции, определенные для них, но должны быть просто конвертируемыми в базовый тип. Таким образом, код как for(auto x : expr)
потерпит неудачу во время компиляции (так как expr
не повторяется), но написание for(auto x : static_cast<vector<int>>(expr))
работает, потому что выражение уже оценено.
Если вы надеялись использовать циклы for на основе диапазона для реализации операций шаблона выражения, то вы можете предоставить закрытый или защищенный begin
а также end
методы в вашем классе шаблонов выражений. Просто убедитесь, что каждый класс шаблона может получить доступ к begin
а также end
методы других шаблонных классов. В этом контексте все должно быть в порядке, поскольку шаблон выражения является параметром функции, поэтому вам не придется беспокоиться о висячих ссылках при написании цикла внутри этой функции.
Как правило, с этим ничего не поделаешь. Если вы задаете выражение в качестве диапазона, оно должно разрешаться до того, что будет действительным после инициализации for
заявление. И нет никакого способа обнаружить во время компиляции, что какой-либо конкретный тип был выведен auto
,
Было бы лучше сделать вашу систему выражений более ориентированной на перемещение, чтобы она не содержала ссылки. Это даст гораздо более безопасные результаты с auto
чем пытаться хранить ссылки на потенциально мертвые вещи. Если вас беспокоит копирование для неподвижных типов, просто живите с этим.