Реализация gcc not_fn: почему _Not_fn принимает дополнительный параметр int? - PullRequest
0 голосов
/ 08 мая 2018

Недавно я взглянул на реализацию из 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 ]

0 голосов
/ 08 мая 2018

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

0 голосов
/ 06 ноября 2018

Не по теме 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 явно не объявляет никакой ctor

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

0 голосов
/ 08 мая 2018

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

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

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

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

...