Обнаружение небезопасной привязки константной ссылки в C ++ - PullRequest
0 голосов
/ 05 марта 2019

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

extern SomeStructure someStructureGenerator();

class ObjectWhichUsesStructure {
  ObjectWhichUsesStructure(const SomeStructure& ref): s(ref) {}
  const SomeStructure& s;
}

ObjectWhichUsesStructure obj(someStructureGenerator());

Я рассуждал так: someStructureGenerator() возвращает временное;это связано с константной ссылкой, что означает, что компилятор продлевает время жизни временного файла в соответствии с местом использования;Я использую его для создания объекта, поэтому временное время жизни увеличивается, чтобы соответствовать сроку жизни объекта.

Этот последний бит оказывается не таким.Как только конструктор завершает работу, компилятор удаляет временный файл, и теперь obj содержит ссылку на гиперпространство, что приводит к веселым результатам, когда я пытаюсь его использовать.Мне нужно явно связать константную ссылку с областью действия, например:

const auto& ref = someStructureGenerator();
ObjectWhichUsesStructure obj(ref);

Это не тот вопрос, о котором я спрашиваю.

Я спрашиваю о следующем: мойкомпилятор gcc 8, я строю с -Wall, и был совершенно счастлив , чтобы скомпилировать приведенный выше код - чисто, без предупреждений.Моя программа работала успешно (но неправильно) под valgrind, также без предупреждений.

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

1 Ответ

0 голосов
/ 05 марта 2019

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

Поэтому возможно выполнить ожидаемое расширениечерез агрегатную инициализацию :

struct ObjectWhichUsesStructure {
  const SomeStructure &s;
};

ObjectWhichUsesStructure obj{someStructureGenerator()};

Здесь нет параметра конструктора (потому что вообще нет конструктора!) и, таким образом, происходит только одно, желаемое связывание.

Стоит понять, почему компилятор не предупреждает об этом: даже если конструктор сохраняет ссылку на временный аргумент, есть допустимых ситуаций , где это работает:

void useWrapper(ObjectWhichUsesStructure);
void f() {useWrapper(someStructureGenerator());}

Здесь SomeStructure живет до конца отчета, в течение которого useWrapper может выгодно использовать ссылку в ObjectWhichUsesStructure.

За счет , запрещающего Приведенные выше допустимые варианты использования позволяют компилятору перехватить проблемный случай, предоставив удаленный конструктор со ссылкой на rvalue:

struct ObjectWhichUsesStructure {
  ObjectWhichUsesStructure(const SomeStructure& ref): s(ref) {}
  ObjectWhichUsesStructure(SomeStructure&&)=delete;
  const SomeStructure& s;
};

Это может стоить сделать temporaкак диагностическая мера без постоянного ограничения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...