Реализация gcc not_fn: почему _Not_fn принимает дополнительный параметр int?

Недавно я взглянул на реализацию std::not_fn Шаблон функции предоставлен gcc.

Тип возвращаемого значения этого шаблона функции _Not_fn - шаблон класса-обертки, который отрицает обернутый вызываемый объект.

Оказывается, что _Not_fn конструктор принимает дополнительный 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?

3 ответа

Решение

Добавлен фиктивный параметр, потому что разработчик не хотел, чтобы идеальный конструктор пересылки был лучше, чем конструктор копирования для не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 Параметр решает эту головную боль.

Живой пример

Изменение было внесено для исправления ошибки 70564.

Я могу думать о двух причинах.

Первая причина в том, что конструктор, который принимает 2 аргумента, не является конвертирующим конструктором. Даже явные преобразования могут иногда вызываться или выбираться для перегрузки случайно. Добавляя int, вопросы о конвертируемости понятны (это не так).

Вторая причина может заключаться в том, что это унаследованный код из трюка упорядочения разрешения перегрузки. Если вы создаете две перегрузки, одна из которых занимает int другой ..., int будет выбран за ... когда оба жизнеспособны.

Если бы в какой-то момент тип имел более сложную конструкцию, он мог бы иметь int а также ... перегрузки. int может быть просто наследие неочищенного кода.

Вне темы sidenote: 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};
    }

Но libcxx не объявляет какой-либо явно

  __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.

Другие вопросы по тегам