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