пятница, 17 февраля 2023 г.

[prog.c++] Вроде бы придумался хороший вопрос про C++ для собеседований

Вчера довелось заглянуть в 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; }
 
  templateclass... 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, то можно попросить переписать шаблонный конструктор посредством концептов.

Короче говоря, если бы мне на собеседовании такой вопрос задали, то это было бы интереснее, чем запрограммировать в онлайне какую-то около олимпиадную задачку. Не говоря уже про банальности о виртуальном деструкторе :)

10 комментариев:

night beast комментирует...

А потом еще немного подумаешь, и решишь "ну их всех в баню, пойду в [s]прости[/s] rust" )
Не потому что сложно, а потому что хочется решать реальные проблемы

eao197 комментирует...

Для решения реальных проблем еще 20 лет назад можно было бы уйти в Java. Еще лет за пять до того можно было уйти в FoxPro/Clipper или 1С :)

А вот из-за чего реально хочется уйти из C++, так это из-за CMake... :(((

night beast комментирует...

В Java и др. нельзя -- у них gc

Ну а отсутствие нормальной системы сборки/пакетирования -- это та проблема, которую, видимо, никто и не собирается решать.

Куда важнее ренджи или еще какую ерунду в стандарт протащить, чем сделать систему, в которой пользователь сам будет решать какие ренджи ему нужны.

eao197 комментирует...

@night beast:

> сделать систему, в которой пользователь сам будет решать какие ренджи ему нужны

Мне кажется, что Microsoft со своим vcpkg нужное дело делает.

Хотя сам бы я лично предпочел бы server-less менеджер зависимостей для C++.

night beast комментирует...

> Мне кажется, что Microsoft со своим vcpkg нужное дело делает.

нужное. и cmake тоже нужное.
только это все суррогаты.

каждый понимает что надо как-то решать проблему и пытается это делать самостоятельно.

eao197 комментирует...

@night beast:

CMake редкостное Г, лучше бы его закопать.

А вот почему vcpkg суррогат?

night beast комментирует...

потому что, имхо, нужно стандартное решение на уровне языка

по типу cargo/pip/etc

eao197 комментирует...

@night beast:

Свежий комментарий на Хабре от Антона Полухина:

"Работа идёт, и она в приоритете для C++26.

Сейчас комитет отошёл от идеи стандартизировать какой-то имеющийся пакетный менеджер или систему сборки. Вместо этого сосредоточились на создании стандартного языка описания зависимостей и сборки. Чтобы с таким файлом описания вы могли собрать проект и с помощью cmake, и с помощью basel, и с помощью conan..."

night beast комментирует...

ну дай бог...

eao197 комментирует...

@night beast

Ага. Рефлексия и pattern matching тоже давным давно в работе :)