[intrusive_ptr] Strange ternary op behavior with MSVC14

Hi, I found a strange bug while debugging: This is the minimal example that shows the problem: (the nullptr will be some other meaningful thing in real case) ```c++ #include <iostream> #include <boost/smart_ptr/intrusive_ptr.hpp> #include <boost/smart_ptr/intrusive_ref_counter.hpp> template<class T> struct wrapper { wrapper() = default; wrapper(boost::intrusive_ptr<T> const& p) : _p(p) {} boost::intrusive_ptr<T> const& get_ptr() const { #if 0 // This is OK. if (_p) return _p; return nullptr; #else // Strange behavior. return _p ? _p : nullptr; #endif } private: boost::intrusive_ptr<T> _p; }; struct W : boost::intrusive_ref_counter<W> { }; template<class T1, class T2> inline void test(wrapper<T1> const& s1, wrapper<T2> const& s2) { auto tup = std::make_tuple(s1.get_ptr(), s2.get_ptr()); std::cout << 0 << ":" << (void*)std::get<0>(tup).get() << "\n"; std::cout << 1 << ":" << (void*)std::get<1>(tup).get() << "\n"; } int main() { wrapper<W> a(new W), b(new W); test(a, b); return 0; } ``` If compiled with MSVC14, you'll see the 2 addresses are the same while they shouldn't. If if-stmt is used instead, the problem won't be shown. I also replaced intrusive_ptr with std::shared_ptr and the problem isn't shown as well. The same code works well with g++. It seems intrusive_ptr & MSVC14 specific but I can't reason about... Any idea?

On 08/11/2015 10:27 AM, TONGARI J wrote:
template<class T> struct wrapper { wrapper() = default;
wrapper(boost::intrusive_ptr<T> const& p) : _p(p) {}
boost::intrusive_ptr<T> const& get_ptr() const { #if 0 // This is OK. if (_p) return _p; return nullptr; #else // Strange behavior. return _p ? _p : nullptr; #endif }
In both cases, you return a reference to a temporary, incurring undefined behavior.

2015-08-11 17:39 GMT+08:00 Stephan Bergmann <sbergman@redhat.com>:
On 08/11/2015 10:27 AM, TONGARI J wrote:
template<class T> struct wrapper { wrapper() = default;
wrapper(boost::intrusive_ptr<T> const& p) : _p(p) {}
boost::intrusive_ptr<T> const& get_ptr() const { #if 0 // This is OK. if (_p) return _p; return nullptr; #else // Strange behavior. return _p ? _p : nullptr; #endif }
In both cases, you return a reference to a temporary, incurring undefined behavior.
Ah damn! You're right, how could I overlook the const& part!? Sorry for the noise.

On 11/08/2015 21:39, Stephan Bergmann wrote:
On 08/11/2015 10:27 AM, TONGARI J wrote:
template<class T> struct wrapper { wrapper() = default;
wrapper(boost::intrusive_ptr<T> const& p) : _p(p) {}
boost::intrusive_ptr<T> const& get_ptr() const { #if 0 // This is OK. if (_p) return _p; return nullptr; #else // Strange behavior. return _p ? _p : nullptr; #endif }
In both cases, you return a reference to a temporary, incurring undefined behavior.
_p is a field, so it's not a temporary as long as its instance isn't. get_ptr() is invoked on named instances that live longer than the test() function, so they're not temporaries either. While granted it's probably not a good idea to create a tuple from references, I don't actually see any undefined behaviour in this particular example. What am I missing?

2015-08-12 7:29 GMT+08:00 Gavin Lambert <gavinl@compacsort.com>:
On 11/08/2015 21:39, Stephan Bergmann wrote:
On 08/11/2015 10:27 AM, TONGARI J wrote:
template<class T> struct wrapper { wrapper() = default;
wrapper(boost::intrusive_ptr<T> const& p) : _p(p) {}
boost::intrusive_ptr<T> const& get_ptr() const { #if 0 // This is OK. if (_p) return _p; return nullptr; #else // Strange behavior. return _p ? _p : nullptr; #endif }
In both cases, you return a reference to a temporary, incurring undefined behavior.
_p is a field, so it's not a temporary as long as its instance isn't. get_ptr() is invoked on named instances that live longer than the test() function, so they're not temporaries either.
While granted it's probably not a good idea to create a tuple from references, I don't actually see any undefined behaviour in this particular example. What am I missing?
The nullptr -> boost::intrusive_ptr<T> makes it a temporary.
participants (3)
-
Gavin Lambert
-
Stephan Bergmann
-
TONGARI J