18
votes

Why does the following code prints "0" as the output?

#include <functional>
#include <iostream>

int main()
{
    typedef void (*fp_t)();

    fp_t fp = nullptr;

    std::function<void()> f = fp;
    std::cout << (f == nullptr) << '\n';
}

I've tested it both with gcc 4.7.2 and MSVC-11.0.

I think that it's should print "1" because of the following quote from the standard:

ISO/IEC 14882:2011

20.8.11.2.1 function construct/copy/destroy [func.wrap.func.con]

template<class F> function(F f);
template<class F, class A> function(allocator_arg_t, const A& a, F f);

...

8 Postconditions: !*this if any of the following hold: — f is a NULL function pointer. — f is a NULL pointer to member. — F is an instance of the function class template, and !f

2
!*this does not imply that this == nullptr is true. One uses operator bool and the other uses operator==. - chris
I would say they should compare equal, though. Equality to nullptr compares true if it's empty, and initializing it with a null function pointer makes it empty. - chris
@chris: According to 20.8.11.2.6/1, f == nullptr returns !f. I tend to believe this is a bug - Andy Prowl
@AndyProwl, Agreed. My first comment was solely based on which function was called and that they don't always have to do the same thing. In the case of std::function, it turns out they do. - chris

2 Answers

15
votes

I think this is a bug. Per paragraph 20.8.11.2.6/1 of the C++11 Standard:

template <class R, class... ArgTypes>
bool operator==(const function<R(ArgTypes...)>& f, nullptr_t) noexcept;

template <class R, class... ArgTypes>
bool operator==(nullptr_t, const function<R(ArgTypes...)>& f) noexcept;

1 Returns: !f.

Therefore, (f == nullptr) should evaluate to true if and only if !f evaluates to true. Then, paragraph 20.8.11.2.1/8 specifies:

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

[...]

8 Postconditions: !*this if any of the following hold:

f is a NULL function pointer.

[...]

Since fp is a null function pointer, the above paragraph should guarantee that after initialization of f from fp, the expression !f evaluates to true. Which in turn means, that the comparison with nullptr should return true (by § 20.8.11.2.6/1).

Which in turns means, that this is a bug.

4
votes

Not an answer, but some details (gcc) too large for a comment:

Function is checked for validity with

template<typename _Signature>
  static bool
  _M_not_empty_function(const function<_Signature>& __f)
  { return static_cast<bool>(__f); }

template<typename _Tp>
  static bool
  _M_not_empty_function(const _Tp*& __fp)
  { return __fp; }

template<typename _Class, typename _Tp>
  static bool
  _M_not_empty_function(_Tp _Class::* const& __mp)
  { return __mp; }

template<typename _Tp>
  static bool
  _M_not_empty_function(const _Tp&)
  { return true; }

Probably

template<typename _Tp>
  static bool
  _M_not_empty_function(const _Tp*& __fp)
  { return __fp; }

is intended to work with functional pointers, buit it doesn't. Instead general case is used that is probably intended for functional objects only.

template<typename _Tp>
    static bool
    M_not_empty_function(const _Tp*& __fp)
    { return __fp; }

int main()
{
    typedef void (*fp_t)();
    fp_t fp = nullptr;
    return  M_not_empty_function(fp);
}

generates

error: no matching function for call to 'M_not_empty_function(void (*&)())'
note: candidate is:
note: template<class _Tp> bool M_not_empty_function(const _Tp*&)
note:   template argument deduction/substitution failed:
note:   types 'const _Tp' and 'void()' have incompatible cv-qualifiers