gcc not_fn 实现:为什么 _Not_fn 接受额外的 int 参数?

gcc not_fn implementation: why does _Not_fn accept additional int parameter?

最近看了一下gcc提供的implementation of std::not_fn函数模板。

此函数模板的 return 类型是 _Not_fn - 包装器 class 模板,它否定包装的可调用对象。

事实证明,_Not_fn constructor 接受一个未明确使用的额外 int 参数:

template<typename _Fn2>
    _Not_fn(_Fn2&& __fn, int)
    : _M_fn(std::forward<_Fn2>(__fn)) { }

对构造函数的调用如下所示:

template<typename _Fn>
inline auto not_fn(_Fn&& __fn) 
    noexcept(std::is_nothrow_constructible<std::decay_t<_Fn>, _Fn&&>::value)
{
    return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0}; // <- 0 is passed here
}

问题:

此附加 int 参数的用途是什么?为什么 gcc 实现需要它?

我可以想到两个原因。

第一个原因是,接受两个参数的构造函数不是转换构造函数。有时甚至会意外调用或选择显式转换以进行重载。通过添加 int,有关可兑换性的问题很明确(不是)。

第二个原因可能是它是来自重载解析排序技巧的遗留代码。如果您创建两个重载,一个需要 int,另一个需要 ...,当两者都可行时,int 将优先于 ...

如果在某一时刻该类型具有更复杂的构造,它可能有 int... 重载。 int 可能只是未清理代码的遗留问题。

添加虚拟参数是因为实施者不希望完美的转发构造函数是 better match 而不是非 const 参数的复制构造函数。

考虑这个例子

struct _Not_fn
{
    template<typename _Fn2>
    _Not_fn(_Fn2&&)
    { /* */ }

    _Not_fn(const _Not_fn&)
    { /* */ }
};

_Not_fn f([]{});
_Not_fn f1(f);         // calls perfect forwarding constructor
_Not_fn f2(const_cast<const _Not_fn&>(f));    // calls copy constructor

移动构造函数也存在同样的问题。虚拟 int 参数的引入解决了这个令人头疼的问题。

Live example

change was introduced to fix bug 70564.

一个题外话的旁注:libstdcx++ 明确定义了这些 ctors:

template<typename _Fn2>
    _Not_fn(_Fn2&& __fn, int)
    : _M_fn(std::forward<_Fn2>(__fn)) { }

      _Not_fn(const _Not_fn& __fn) = default;
      _Not_fn(_Not_fn&& __fn) = default;
      ~_Not_fn() = default;

/*other member functions*/


   template<typename _Fn>
    inline auto
    not_fn(_Fn&& __fn)
    noexcept(std::is_nothrow_constructible<std::decay_t<_Fn>, _Fn&&>::value)
    {
      return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0};
    }

But libcxx doesn't declare any ctor explicitly

  __not_fn_imp() = delete;

__not_fn_imp ┃ ┃ (Class) ┃ ┃ operator() (Method) ┃ ┃ operator() (Method) ┃ ┃ operator() (Method) ┃ ┃ operator() (Method) ┃ ┃ __fd (Field)

所以,

template <class _RawFunc>
inline _LIBCPP_INLINE_VISIBILITY __not_fn_imp<decay_t<_RawFunc> >
not_fn(_RawFunc&& __fn) {
  return __not_fn_imp<decay_t<_RawFunc> >(_VSTD::forward<_RawFunc>(__fn));
}

可以找到正确的 (cpoy)ctor。