Список инициализаторов в квадратных скобках C , временное время жизни

#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 «временная привязка к ссылочному элементу в списке инициализаторов конструктора сохраняется только до завершения работы конструктора, а не до тех пор, пока существует объект». здесь имеет приоритет.