23

I understand that if a temporary is bound to a reference member in the constructor's initializer list, the object will be destroyed as the constructor returns.

However, consider the following code:

#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;
}

This looks perfectly valid to me. The callback object will live during the whole execution of the func function and no temporary copy should be made for wrapper's constructor.

Indeed, GCC 4.9.0 compiles fine with all warnings enabled.

However, GCC 4.8.2 compiler gives me the following warning:

$ 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} { }
                                            ^

Is this a false positive or am I misunderstanding the object lifetimes?

Here are my exact compiler versions tested:

$ 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.
user3666197
  • 1
  • 6
  • 50
  • 92
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • Turning off optimizations gives me a seg fault. Valgrind points out that the problem is somewhere around `func(std::function const&)::wrapper::call()`. –  Aug 29 '14 at 03:55
  • 2
    Using `w_cb {cb}` results in segmentation violation for me. Using `w_cb(cb)` doesn't suffer from the same problem. Tested in g++ 4.8.3. – R Sahu Aug 29 '14 at 04:01
  • I could reproduce the Valgrind error with GCC 4.8.2. (Not a segfault, tough, the program outputs 42 and exits successfully as expected.) The executable produced by GCC 4.9.0 is Valgrind-clean. These observations don't change with different optimization levels. – 5gon12eder Aug 29 '14 at 04:12
  • 1
    I was shocked to hear it failed on some systems. FWIW, works fine on ideone's default settings (GCC 4.8.1, with `{cb}`). – Tony Delroy Aug 29 '14 at 04:35

2 Answers2

14

This is a bug in gcc 4.8 that has been fixed in 4.9. Here is the bug report:

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

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    The fact that the bug is not the *warning*, but rather what the *warning* is warning you about, would be useful. I had to go to the link to determine that... Ie, a temporary is actually created in gcc 4.8 and it doesn't last long enough. Optimizations may cause the temporary to be elided out of existence? – Yakk - Adam Nevraumont Aug 29 '14 at 14:32
  • Yes, this explains what is going on. Thanks for the link. I did some further research inspired by it and will post it as an answer but I'll accept yours. – 5gon12eder Aug 31 '14 at 20:11
  • 5
    It was a bug in the standard, and GCC was implementing the precise wording of the standard, which _required_ a temporary to be created there. DR 1288 fixed the standard. – Jonathan Wakely Aug 31 '14 at 20:37
7

As pointed out by Howard Hinnant and already indicated by R Sahu's comment, this is a bug (which used to be required by the then-broken standard; thanks to Tony D for pointing this out) in the way GCC 4.8 is treating initializer lists.

Changing the constructor in my original example from

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

to

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

makes the warning with GCC 4.8.3 go away and the created executable Valgrind clean. The diff of the two assembly files is huge so I don't post it here. GCC 4.9.0 creates identical assembly code for both versions.

Next, I replaced the std::function with a user-defined struct and deleted copy and move constructors and assignment operators. Indeed, with GCC 4.8.3, this retains the warning but now also gives a (slightly more helpful) error that the above line of code calls the deleted copy constructor of the struct. As expected, there is no difference with GCC 4.9.0.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92