Std:: function constructor и nullptr
Почему следующий код печатает "0" в качестве вывода?
#include <functional>
#include <iostream>
int main()
{
typedef void (*fp_t)();
fp_t fp = nullptr;
std::function<void()> f = fp;
std::cout << (f == nullptr) << '\n';
}
Я тестировал его как с gcc 4.7.2, так и с MSVC-11.0.
Я думаю, что он должен печатать "1" из-за следующей цитаты из стандарта:
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 Постусловия: !*this
, если выполнено одно из следующих условий: - f
- NULL
указатель функции. - f
является указателем NULL
для члена. - f
- это экземпляр шаблона класса функций и !f
Ответы
Ответ 1
Я думаю, что это ошибка. В соответствии с пунктом 20.8.11.2.6/1 стандарта С++ 11:
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 Возвращает: !f
.
Следовательно, (f == nullptr)
должен оцениваться до true
тогда и только тогда, когда !f
оценивается как true
. Затем в пункте 20.8.11.2.1/8 указывается:
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
[...]
8 Постусловия: !*this
если выполнено одно из следующих значений:
- f
является указателем функции NULL.
[...]
Так как fp
является указателем на нулевую функцию, вышеприведенный параграф должен гарантировать, что после инициализации f
из fp
выражение !f
оценивается как true
. Это, в свою очередь, означает, что сравнение с nullptr
должно возвращать true
(в соответствии с § 20.8.11.2.6/1).
Что по очереди означает, что это ошибка.
Ответ 2
Не ответ, но некоторые подробности (gcc) слишком большие для комментария:
Функция проверяется на достоверность с помощью
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; }
Вероятно
template<typename _Tp>
static bool
_M_not_empty_function(const _Tp*& __fp)
{ return __fp; }
предназначен для работы с функциональными указателями, buit - нет. Вместо этого используется общий случай, который, вероятно, предназначен только для функциональных объектов.
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);
}
генерирует
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