Может ли захват по ссылке в шаблонах выражений сосуществовать с выводом типа?

Шаблоны выражений часто используются как метод оптимизации, чтобы избежать создания временных объектов. Они откладывают создание завершенного объекта до тех пор, пока шаблон не будет использован в присваивании или инициализации. Это находит применение в строителях строк, пакетах линейной алгебры и т. Д.

Чтобы избежать дорогих копий, класс шаблона выражения может захватывать большие аргументы по ссылке. Я буду использовать Qt’s QStringBuilder В качестве примера.

Это работает, когда ссылки переживают шаблон выражения:

QString foo = QString("A") + QString("B");
^^^^^^^^^^^^^^^^^^^^^^^^^^^
QStringBuilder<QConcatenable<QString>,
QConcatenable<QString>>

Преобразование и разрешение шаблона выражения происходит при назначении. Временные строки переживают задание.

Увы, мы сталкиваемся с проблемами, как только выводится тип шаблона выражения вместо целевого типа:

// WORKS
QString foo = []() -> QString { return QString("A") + QString("B"); }();
// FAILS
QString foo = []{ return QString("A") + QString("B"); }();

А также:

auto foo = QString("A") + QString("B");
// foo holds references to strings that don't exist anymore
QString bar = foo; // oops

Одно из решений для строителя — хранить копии объектов. поскольку QStringЗдесь неявно делятся, их копирование обходится дешево, хотя и дороже, чем хранение ссылки. Предположим, однако, что аргументы были std::string: вы определенно не хотите копировать их без необходимости.

Есть ли какой-либо метод, который можно использовать для обнаружения того, что полное выражение шаблона не разрешается немедленно и должно копировать данные, к которому пока что имеется только ссылка?

Примечание: я не спрашиваю о какой-либо конкретной существующей реализации шаблонов выражений. Я использую только QStringBuilder как мотивирующий пример. Это не вопрос Qt или собственный вопрос и т. Д. Заголовок — это вопрос, в значительной степени.

8

Решение

Вы не можете надежно определить, когда ссылка на объект становится недействительной, если объект каким-либо образом не указывает на то, что он будет признан недействительным. Также вы не можете заранее определить, есть ли какая-то конкретная функция будут Чтобы вызывать ваши объекты выражения, вы можете обнаружить, что он был вызван, только когда он действительно вызывается.

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

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

В конечном счете, я думаю, что вы пытаетесь использовать технические средства для решения нетехнической проблемы: вы решили (разумно, IMO), что не хотите копировать ваши данные при построении выражений, даже когда данные объекты используют COW. Вам необходимо либо проинформировать своих пользователей о последствиях этого решения, либо пересмотреть свое решение.

2

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

Есть ли какой-либо метод, который можно использовать для обнаружения того, что полное выражение шаблона не разрешается немедленно и должно копировать данные, к которому пока что имеется только ссылка?

Это происходит в основном, когда вы копируете Builderтак что вы можете управлять этим в вашем конструкторе копирования:

struct Expr
{
explicit Expr(const std::string& s) : s(s) {};

const std::string& s;
};

struct ExprBuilder
{
// deleted
//~~or provide implementation which copy operand (or result)~~ RVO may avoid that fix
ExprBuilder(const ExprBuilder&) = delete;
ExprBuilder& operator = (const ExprBuilder&) = delete;

ExprBuilder(const Expr& lhs, const Expr& rhs) : lhs(lhs), rhs(rhs) {}

operator std::string() const { return lhs.s + rhs.s; }

Expr lhs;
Expr rhs;
};ExprBuilder operator + (const Expr& lhs, const Expr& rhs)
{
return {lhs, rhs};
}

int main() {
std::string s = Expr("hello") + Expr(" world");

std::cout << s << std::endl;
auto builder = Expr("hello") + Expr(" world"); // Use of copy constructor here
s = builder;
std::cout << s << std::endl;

std::string foo =
[]{ return Expr("A") + Expr("B"); } // Use of copy constructor here
();
}

но все равно не удается, когда вы продлите жизнь временного с помощью констант

const auto& builder = Expr("hello") + Expr(" world"); // Undetected error here
0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector