С++ время жизни временных файлов - это безопасно?

Если я правильно понимаю правила для времени жизни временных рядов, этот код должен быть безопасным, так как время жизни временного stringstream в make_string() продолжается до конца полного выражения. Я не на 100% уверен, что здесь нет тонкой проблемы, может ли кто-нибудь подтвердить, безопасен ли этот шаблон использования? Кажется, что он отлично работает в clang и gcc.

#include <iomanip>
#include <iostream>
#include <sstream>

using namespace std;

ostringstream& make_string_impl(ostringstream&& s) { return s; }

template<typename T, typename... Ts>
ostringstream& make_string_impl(ostringstream&& s, T&& t, Ts&&... ts) {
    s << t;
    return make_string_impl(std::move(s), std::forward<Ts>(ts)...);
}

template<typename... Ts>
string make_string(Ts&&... ts) {
    return make_string_impl(ostringstream{}, std::forward<Ts>(ts)...).str();
}

int main() {
    cout << make_string("Hello, ", 5, " World!", '\n', 10.0, "\n0x", hex, 15, "\n");
}

Ответ 1

Соответствующая часть стандарта находится в §12.2:

12.2.3) Временные объекты уничтожаются как последний шаг при оценке полного выражения (1.9), которое (лексически) содержит точку, в которой они были созданы.

Кроме:

12.2.4) Существует два контекста, в которых временные объекты уничтожаются в другой точке, чем конец полного выражения. Первый контекст - это когда вызывается конструктор по умолчанию для инициализации элемента массива.... [ не применяется]

12.2.5) Второй контекст - это когда ссылка привязана к временному. Временное, к которому привязана ссылка, или временное, являющееся полным объектом подобъекта, к которому привязана ссылка, сохраняется для времени жизни ссылки, за исключением:

  • ...

  • Временная привязка к эталонному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов.

Итак, вы идете. Временная stringstream{} привязана к ссылке в вызове функции, поэтому она сохраняется до завершения выражения. Это безопасно.