Вчера довелось заглянуть в cppreference в описание стандартного класса std::reference_wrapper. И взгляд зацепился за показанную там возможную реализацию этого класса:
namespace detail { template <class T> constexpr T& FUN(T& t) noexcept { return t; } template <class T> void FUN(T&&) = delete; } template <class T> class reference_wrapper { public: // types using type = T; // construct/copy/destroy template <class U, class = decltype( detail::FUN<T>(std::declval<U>()), std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>() )> constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u)))) : _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {} reference_wrapper(const reference_wrapper&) noexcept = default; // assignment reference_wrapper& operator=(const reference_wrapper& x) noexcept = default; // access constexpr operator T& () const noexcept { return *_ptr; } constexpr T& get() const noexcept { return *_ptr; } template< class... ArgTypes > constexpr std::invoke_result_t<T&, ArgTypes...> operator() ( ArgTypes&&... args ) const noexcept(std::is_nothrow_invocable_v<T&, ArgTypes...>) { return std::invoke(get(), std::forward<ArgTypes>(args)...); } private: T* _ptr; }; |
Внимание мое привлекли реализации конструкторов для reference_wrapper. Как-то сурово там все, сходу и не поймешь. Т.е. приблизительно понятно почему так сложно, но вот как именно это должно работать... За пару-тройку минут и не въедешь.
Чуть позже, уже по дороге домой, посредством неспешных размышлений картинка выстроилась, вроде бы все встало на свои места. И как бы и не сложно там все. Хотя и страшно это конечно же выглядит.
Но потом подумалось вот что: а ведь если на собеседовании по C++ у соискателя попросить объяснить почему у предложенной реализации reference_wrapper такие конструкции и как именно это работает, то получится отличный вопрос на знание C++.
Действительно, тут тебе и выбор между шаблоном и не шаблоном, и SFINAE, и std::declval, и оператор запятая, и тип выражения, в котором оператор запятая используется, и про noexcept, и еще раз про noexcept, но уже другой, и std::addressof вместо &, и еще всякое разное по мелочи.
Как по мне, так если человек слабо знает C++, то он толком ничего объяснить не сможет.
Если знания C++ средненькие, как у меня, например, то потихоньку выплывет. Заодно интервьюер сможет послушать как соискатель размышляет в процессе. Это же, типа, очень важно, можно и молодежно, как же без этого ;)
Ну а если перед вами человек, который с C++ на "ты", то для него это вообще проблемы не составит. Может он даже расскажет, как сделать тоже самое более простым и красивым способом (disclaimer: я даже не имею понятия можно ли вообще).
В случае, если требуется еще и знание C++20, то можно попросить переписать шаблонный конструктор посредством концептов.
Короче говоря, если бы мне на собеседовании такой вопрос задали, то это было бы интереснее, чем запрограммировать в онлайне какую-то около олимпиадную задачку. Не говоря уже про банальности о виртуальном деструкторе :)