Ложное предупреждение о временной привязке к элементу ссылки в конструкторе

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

Однако рассмотрите следующий код:

#include <functional>
#include <iostream>

using callback_func = std::function<int(void)>;

int
func(const callback_func& callback)
{
  struct wrapper
  {
    const callback_func& w_cb;
    wrapper(const callback_func& cb) : w_cb {cb} { }
    int call() { return this->w_cb() + this->w_cb(); }
  };
  wrapper wrp {callback};
  return wrp.call();
}

int
main()
{
  std::cout << func([](){ return 21; }) << std::endl;
  return 0;
}

Это выглядит совершенно справедливо для меня. Объект callback будет жить в течение всего выполнения функции func, и для конструктора wrapper не нужно делать временную копию.

Действительно, GCC 4.9.0 отлично компилируется со всеми включенными предупреждениями.

Однако компилятор GCC 4.8.2 выдает следующее предупреждение:

$ g++ -std=c++11 -W main.cpp 
main.cpp: In constructor ‘func(const callback_func&)::wrapper::wrapper(const callback_func&)’:
main.cpp:12:48: warning: a temporary bound to ‘func(const callback_func&)::wrapper::w_cb’ only persists until the constructor exits [-Wextra]
     wrapper(const callback_func& cb) : w_cb {cb} { }
                                            ^

Это ложное срабатывание или я неправильно понимаю время жизни объекта?

Вот мои точные проверенные версии компилятора:

$ g++ --version
g++ (GCC) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ g++ --version
g++ (GCC) 4.9.0 20140604 (prerelease)
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

person 5gon12eder    schedule 29.08.2014    source источник
comment
Отключение оптимизации дает мне ошибку seg. Валгринд указывает, что проблема где-то в районе func(std::function<int ()> const&)::wrapper::call().   -  person    schedule 29.08.2014
comment
Использование w_cb {cb} приводит к нарушению сегментации для меня. Использование w_cb(cb) не страдает от той же проблемы. Протестировано в g++ 4.8.3.   -  person R Sahu    schedule 29.08.2014
comment
Я смог воспроизвести ошибку Valgrind с GCC 4.8.2. (Не segfault, жестко, программа выводит 42 и успешно завершает работу, как и ожидалось.) Исполняемый файл, созданный GCC 4.9.0, является Valgrind-чистым. Эти наблюдения не меняются при разных уровнях оптимизации.   -  person 5gon12eder    schedule 29.08.2014
comment
Я был потрясен, узнав, что это не удалось на некоторых системах. FWIW, отлично работает с настройками ideone по умолчанию (GCC 4.8.1, с {cb}).   -  person Tony Delroy    schedule 29.08.2014


Ответы (2)


Это ошибка в gcc 4.8, которая была исправлена ​​в версии 4.9. Вот отчет об ошибке:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=50025

person Howard Hinnant    schedule 29.08.2014
comment
Тот факт, что ошибка — это не предупреждение, а скорее то, о чем вас предупреждает предупреждение, был бы полезен. Мне пришлось перейти по ссылке, чтобы определить, что... Т.е. в gcc 4.8 фактически создан временный файл, и он не длится достаточно долго. Оптимизация может привести к тому, что временное исчезнет из существования? - person Yakk - Adam Nevraumont; 29.08.2014
comment
Да, это объясняет, что происходит. Спасибо за ссылку. Я провел дополнительное исследование, вдохновленное этим, и опубликую его как ответ, но я приму ваш. - person 5gon12eder; 01.09.2014
comment
Это была ошибка в стандарте, и GCC реализовывал точную формулировку стандарта, которая требовала создания там временного файла. DR 1288 зафиксировал стандарт. - person Jonathan Wakely; 01.09.2014

Как указал Говард Хиннант и уже указано в комментарии Р. Саху, это ошибка (которая раньше требовалась тогдашним сломанным стандартом; спасибо Тони Д. за указание на это) в том, как GCC 4.8 обрабатывает списки инициализаторов.

Изменение конструктора в моем исходном примере с

wrapper(const callback_func& cb) : w_cb {cb} { }

to

wrapper(const callback_func& cb) : w_cb (cb) { }

убирает предупреждение с GCC 4.8.3 и очищает созданный исполняемый файл Valgrind. Разница между двумя сборочными файлами огромна, поэтому я не публикую ее здесь. GCC 4.9.0 создает идентичный ассемблерный код для обеих версий.

Затем я заменил std::function определяемой пользователем структурой и удалил конструкторы копирования и перемещения и операторы присваивания. Действительно, с GCC 4.8.3 это сохраняет предупреждение, но теперь также дает (чуть более полезную) ошибку, что приведенная выше строка кода вызывает конструктор удаленной копии структуры. Как и ожидалось, разницы с GCC 4.9.0 нет.

person 5gon12eder    schedule 31.08.2014