Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::function constructor and nullptr

Tags:

c++

c++11

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

like image 944
FrozenHeart Avatar asked May 29 '13 20:05

FrozenHeart


2 Answers

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.

like image 119
Andy Prowl Avatar answered Oct 18 '22 14:10

Andy Prowl


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
like image 35
Lol4t0 Avatar answered Oct 18 '22 14:10

Lol4t0