Должен ли компилятор предупреждать о небезопасном поведении при использовании временного объекта с параметрами типа const ref?

Давайте рассмотрим следующий неправильный код:

#include <string>
#include <iostream>

struct C {
C(const std::string &_s): s(_s) { }
void run() {
std::cout << "Here we are using the string " << s << "\n";
}
const std::string &s;
};

int main() {
C a("/tmp/example.log");
a.run();
return 0;
}

Либо g ++ 6.3.0, так как clang 3.8.1 компилирует этот код без предупреждения, даже если -W -Wall -Werror, но даже для неосторожного программиста поведение просто говорит, что что-то здесь не так:

Here we are using the string Here we are usin

Почему результаты не такие, как можно наивно ожидать? Время жизни std::string объект, созданный из const char * ограничено областью конструктора. Так когда foo() конструктор возвращает, foo::s поле имеет ссылку на несуществующий объект. Kaboom.

Если кто-то понимает идею, это легко представить исправление. Это может быть либо явное создание std::string объект и передавая его в качестве параметра:

std::string s("/tmp/example.log");
C a(s);

Другой, который я считаю немного более безопасным, это изменить foo::s объявление поля для:

const std::string s;

создание локальной неизменяемой копии параметра, но это добавляет стоимость копирования объекта. Что ж, в этом примере копирование std :: string автоматически не вызывает глубокое копирование, но для собственного реализованного класса это может быть совсем другое дело.

Для C ++ 11 доступно несколько других методов, как указано в этот ответ, но — например — во встроенной среде Linux, когда вы привязаны к старому компилятору и некоторому устаревшему коду, это не обязательно является значительным вариантом.

Однако, какое бы решение ни выбрал программист, нужно знать, что есть проблема. Даже если я правильно напишу свой код, я глупо предположил, что компилятор будет достаточно мудрым, чтобы обнаружить такие ситуации как явно опасные, если какой-то другой разработчик попадет в ловушку передачи const char * параметр. Однако я решил написать некоторый код для проверки поведения компилятора, и я был разочарован — можно легко попасть в ловушку наличия ссылки на уже несуществующий объект.

Передача временного объекта в качестве константной ссылки является общепризнанной опасностью, и она была каким-то образом решена в C ++ 11, но все же перекладывает ответственность на программиста, чтобы активно противодействовать такому поведению. Итак, разумно ли ожидать предупреждения от компилятора или, скорее, следует полагаться на внешние инструменты статического анализа?

0

Решение

Привязать временные к постоянным ссылочным работам по проекту и не должно выдавать никаких предупреждений. И это не источник проблемы в вашем коде. Проблема в том, что вы используете константную ссылку в качестве члена, а не управляете ее правильностью. И даже если вы пройдете std::string как вы и предполагали, это не устранит проблему — эта строка должна пережить ваш объект, чтобы ваша программа была правильной. И создание члена как std::string или же const std::string Это не мало безопасный, но безопасный и правильный способ сделать это в этой ситуации. Есть случаи, когда вы можете захотеть сохранить постоянную или неконстантную ссылку в качестве участника, но вы должны понимать, что вы делаете, и следить за тем, чтобы ссылка не стала висячей. Компилятор вряд ли обнаружит угловые случаи и выдаст вам предупреждение в этом случае.

3

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

Проблема в том, что IMO не передает временный объект как const ref, а сохраняет его как таковой. Изменение члена на const std::string s; (или даже неконстантный) решит проблему.

Если вы планируете сохранить его в качестве справочного материала, вы должны обеспечить срок его службы снаружи. В самом деле, в C ++ довольно легко выстрелить себе в ногу, если не быть достаточно осторожным.

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

Обратите внимание, что то же самое относится к хранению в качестве указателя и т. Д.

Кроме того, наличие параметра, передаваемого по значению (в отличие от const ref), вообще не решает проблему, поскольку такой параметр, скопированный как значение, все еще является временным, поэтому при привязке к ссылке он также исчезает, когда конструктор оставил.

0

По вопросам рекламы [email protected]