#c #c 11 #initializer-list #temporary-objects
#c #c 11 #список инициализаторов #временные объекты
Вопрос:
У меня есть следующий код:
string join(initializer_list<string_view> strings);
initializer_list — это std::initializer_list, а string_view — это не std::string view, но очень похожий класс с конструкторами из const stringamp; и const char *.
Затем я получаю следующий вызов join
:
EXPECT_EQ("this", join({ string("this") }));
После небольшого исследования я обнаружил, что первым элементом результирующего списка инициализаторов является не "this"
но "his"
. Это связано с тем, что деструктор временного, созданного с помощью string("this")
, вызывается сразу после создания временного string_view
(поэтому он содержит недопустимые указатели). Почему это так, что время жизни string("this")
не продлевается до конца полного выражения EXPECT_EQ("this", join({ string("this") }));
?
Редактировать
Хорошо, как вы и предлагали, есть автономный пример:
#include <iostream>
#include <string>
using namespace std;
class string_view {
public:
string_view(const stringamp; str)
: _begin(str.data())
, _end(str.data() str.size()) {
std::cout << "string_view(...)" << std::endl;
}
const char* _begin;
const char* _end;
};
void join(initializer_list<string_view> strings) {
std::cout << "join(...)" << std::endl;
for (auto i = 0u; i < 5; i) {
std::cout << int(strings.begin()->_begin[i]) << " " << strings.begin()->_begin[i] << std::endl;
}
}
int main() {
join({ string("this") });
return 0;
}
Выходные данные этой программы, скомпилированные с использованием последней версии Visual Studio C (Express):
string_view(...)
join(...)
0
104 h
105 i
115 s
0
Это может варьироваться от компилятора к компилятору, поскольку приведенная выше программа, вероятно, неправильно сформирована.
Я исследовал, каков порядок вызовов в debugger, и вот последовательность:
main()
basic_string(const char*)
string_view(const stringamp;)
~basic_string()
initializer_list(...)
join(...)
Я бы хотел, чтобы содержимое string("this")
было доступно внутри join
функции. И это не так, потому что строка `(«this») была уничтожена ранее.
Почему деструктор временной строки string("this")
вызывается перед join
вызовом функции или, другими словами, почему время жизни string("this")
не продлевается до конца полного выражения join({ string("this") })
?
Комментарии:
1. пожалуйста, приведите автономный пример вашего кода, а не просто одну строку
2. Я не понимаю, почему вы принимаете
initializer_list
аргумент, а не какой-либо тип контейнера, который вы можете инициализировать с помощью списка.3. Если
EXPECT_EQ
является макросом, то невозможно определить, существует ли вообще «полное выражение». Тем не менее, представление string должно существовать только в течениеjoin
вызова, что оно явно и делает, поэтому я подозреваю, что проблема кроется в другом.4. Для справки, время жизни объектов списка инициализаторов и их элементов описано в 8.5.4 /5-6.
5. Джозеф: Если я вас правильно понял, одна из причин, по которой я использую initializer_list вместо, например, vector, заключается в том, что я хочу избежать копирования элементов.
Ответ №1:
Я думаю, что здесь может произойти то, что вы создаете свой initializer_list из ссылки на вашу строку, так что строка выходит из области видимости, когда ваш initializer_list завершает построение.
Вы видите, что ваша строка является параметром не для функции join(), а для созданного вами initializer_list.
Я предполагаю, что создается initializer_list, ваша строка выходит за пределы области видимости, а затем ваша функция join() пытается получить доступ к мертвой строке через ссылку, содержащуюся в string_view.
Комментарии:
1. Временные файлы существуют по крайней мере до конца полного выражения, в котором они были созданы.
2. Да, это то, что, вероятно, здесь происходит, но, по замечанию dyp, этого не должно быть.
3. @dyp Да, но, но это выражение здесь для строки tmp является фактическим аргументом ctors члена списка инициализации, а не самого списка инициализации tmp.
4. @Solkar Я не совсем понимаю, что вы имеете в виду.
join({ string("..") });
является полным выражением иstring("..")
является подвыражением, создающим временное.{ ..}
Создает временныйstd::initializer_list<string_view>
. Оба временных параметра будут (должны) быть уничтожены в конце полного выражения.5. @dyp Я понимаю, что это правило en.cppreference.com/w/cpp/language/reference_initialization «временная привязка к ссылочному элементу в списке инициализаторов конструктора сохраняется только до завершения работы конструктора, а не до тех пор, пока существует объект». здесь имеет приоритет.