Реализация 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?


person Edgar Rokjān    schedule 08.05.2018    source источник
comment
Это достаточно распространено для определения приоритетов перегрузок, когда задействован SFINAE. Возможно, раньше это было реализовано по-другому, и этот параметр оставили.   -  person chris    schedule 08.05.2018


Ответы (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.

person Praetorian    schedule 08.05.2018
comment
Вот коммит: github.com/gcc-mirror/gcc/commit/ - person chris; 08.05.2018
comment
@chris Спасибо, что нашли это! Я связался с PR и фиксацией в ответе. - person Praetorian; 08.05.2018
comment
Теперь это имеет смысл для меня! Я чувствую, что если бы я был разработчиком, я бы сделал ту же ошибку... - person Edgar Rokjān; 08.05.2018
comment
, int определенно лучше написания этого ограничения! - person Barry; 08.05.2018

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

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

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

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

person Yakk - Adam Nevraumont    schedule 08.05.2018

Примечание не по теме: 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)

So,

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.

person 陳 力    schedule 06.11.2018