Хочешь уверенно проходить IT-интервью?

Мы понимаем, как сложно подготовиться: стресс, алгоритмы, вопросы, от которых голова идёт кругом. Но с AI тренажёром всё гораздо проще.
💡 Почему Т1 тренажёр — это мастхэв?
- Получишь настоящую обратную связь: где затык, что подтянуть и как стать лучше
- Научишься не только решать задачи, но и объяснять своё решение так, чтобы интервьюер сказал: "Вау!".
- Освоишь все этапы собеседования, от вопросов по алгоритмам до диалога о твоих целях.
Зачем листать миллион туториалов? Просто зайди в Т1 тренажёр, потренируйся и уверенно удиви интервьюеров. Мы не обещаем лёгкой прогулки, но обещаем, что будешь готов!
Реклама. ООО «Смарт Гико», ИНН 7743264341. Erid 2VtzqwP8vqy
Изучение С++ – дорога длиною в жизнь. Сегодня этот язык является рекордсменом по количеству подводных камней и способов выстрелить себе в ногу. При этом он нереально востребован и является одним из самых быстрых. Мы подготовили несколько советов, которые сделают ваш код более читаемым и оптимизированным.
Автовыведение типа
Несмотря на то, что ключевое слово auto
было введено еще в C++ 11, многие программисты продолжают его игнорировать. А ведь автовыведение позволяет экономить время и делает код лаконичным.
Увидеть преимущества можно даже на примере стандартных типов STL:
//создадим простенький вектор с координатами городов
std::map<std::string, std::pair<float, float>> coordinatesRus;
std::map<std::string, std::pair<float, float>> coordinatesUsa;
coordinatesRus["Moscow"] = std::make_pair(45.2376, 37.2236);
coordinatesRus["Sankt Petersburg"] = std::make_pair(59.9386, 30.3141);
coordinatesUsa["New York"] = std::make_pair(40.7142, -74.0059);
coordinatesUsa["Chikago"] = std::make_pair(41.8500, -87.6500);
std::vector<std::map<std::string, std::pair<float, float>>> coordinatesWorld;
coordinatesWorld.push_back(coordinatesRus);
coordinatesWorld.push_back(coordinatesUsa);
//теперь сделаем обход с двумя разными for
for (std::map<std::string, std::pair<float, float>> country : coordinatesWorld) {
for (std::map<std::string, std::pair<float, float>>::iterator it = country.begin(); it != country.end(); ++it) {
std::cout << it->first << " " << it->second.first << " " << it->second.second << std::endl;
}
}
//и с auto
for (auto country : coordinatesWorld) {
for (auto it = country.begin(); it != country.end(); ++it) {
std::cout << it->first << " " << it->second.first << " " << it->second.second << std::endl;
}
}
Сложный расчет констант
Использование констант – хороший тон. Это позволяет компилятору лучше оптимизировать код и делает его более явным. Но если вычисление слишком громоздкое, от модификатора const
приходится отказываться. На помощь приходят лямбда-функции:
const int myVariable = [&] {
if (contidionA)
return conditionB ? computeFunc(param) : 0;
else
return param * 2;
}();
Трюки с битовыми операциями
Эти приёмы не так полезны в повседневном программировании, но помогут козырнуть на собеседовании или соревнованиях:
//изящное определение четности
if ((n & 1) != 0) {
//нечётный
}
else {
//чётный
}
//свап значений x и y без создания третьей переменной при помощи ксора
x ^= y;
y ^= x;
x ^= y;
//умножение и деление на при помощи битового сдвига
y = x << 2; //x будет умножен на 4
y = x >> 3; //x будет разделен на 8
Трюки с логарифмом
Функцию log
тоже можно использовать для ряда изящных решений:
//определение старшей цифры числа n, если его разрядность неизвестна
double k = log10(n);
k = k - floor(k);
int result = pow(10, k);
//подсчет количества цифр в целом числе без цикла
int digitСount = floor(log10(n)) + 1
Встроенные алгоритмы
Удобно использовать встроенные функции all_of
, any_of
и none_of
для быстрой проверки элементов коллекции на соответствие условию.
//стандартный массив
if (std::all_of(arr, arr + 5, [](int elem) { return elem > 0; }))
std::cout << "Все элементы массива положительны" << std::endl;
//работа с коллекциями
if (std::any_of(someVector.begin(), someVector.end(), [](int elem) { return elem == 0; }))
std::cout << "В векторе присутствует 0" << std::endl;
Трюки с шаблонами
Шаблоны сложнее для понимания, чем наши предыдущие советы, и могут показаться трудночитаемыми, но однозначно стоят потраченного времени.
Проверка специализации типа
Иногда встает задача проверить, является ли конкретный тип данных специализацией определенного шаблона. Для этого можно использовать синтаксис шаблонов template
:
//проверка на пустые параметры
template <template <typename...> class T, typename U>
struct is_specialization_of : std::false_type {};
//создаем шаблон, принимающий в виде аргументов другой шаблон и шаблонный тип данных с неопределенным числом аргументов
template <template <typename...> class T, typename... Us>
struct is_specialization_of<T, T<Us...>> : std::true_type {};
int main(){
//тестируем
std::cout << is_specialization_of<std::tuple, std::tuple<int>>::value;
std::cout << is_specialization_of<std::tuple, std::map<int,std::string>>::value;
}
Проверка возможности конструирования элемента с набором конкретных параметров
Когда шаблонный класс задан в виде template<class T, typename... Args>
, бывает трудно понять, какие аргументы можно использовать. Метод is_constructible
из библиотеки type_traits
даёт неполный ответ: он показывает, существует ли конструктор под конкретные аргументы. Для более полной картины можно использовать еще один шаблон:
template <typename...>
struct identity {};
//проверка на пустые параметры
template <typename, typename, typename = std::void_t<>>
struct is_braces_constructible_impl : std::false_type {};
//определяем соответствие типов
template <typename T, typename... Args>
struct is_braces_constructible_impl < T, identity<Args...>,
std::void_t<decltype(T{ std::declval<Args>()... }) >> : std::true_type {};
template <typename T, typename... Args>
struct is_braces_constructible : is_braces_constructible_impl<T, identity<Args...>> {};
int main(){
std::cout << is_braces_constructible<std::tuple<int, int>, int, int>::value;
std::cout << is_braces_constructible<std::tuple<int, int, int>, int, int>::value;
}
Вызов функции через кортеж
Креативное применение шаблонов, которое позволяет вызвать любую функцию, передав в неё параметры в виде кортежа:
//основной шаблон, позволяющий вызвать функцию по ссылке и передать в неё кортеж
template <typename F, size_t... Idx, typename... Ts>
constexpr auto call_via_tuple_impl(F f, std::index_sequence<Idx...>, const std::tuple<Ts...>& args) {
return f(std::get<Idx>(args)...);
}
//дополнительный шаблон, позволяющий не заботиться о размере кортежа
template <typename F, typename... Ts>
constexpr auto call_via_tuple(F f, const std::tuple<Ts...>& args) {
return call_via_tuple_impl(f, std::make_index_sequence<sizeof...(Ts)>(), args);
}
int main(){
//создаем кортеж
constexpr auto tup = std::make_tuple(6, 33, 1);
//тестируем
auto func = [](int a, int b, int c) -> auto { return a + b + c; }; //можно сделать ссылку на функцию с неопределенным числом параметров
std::cout << call_via_tuple(func, tup);
}
Поиск первого элемента указанного типа в кортеже
Кортежи, в силу своей способности хранить разные типы данных, часто бывают полезной коллекцией. Но эта же особенность может доставлять неудобства. Наш последний шаблон позволит без циклов находить в кортеже элементы определенного типа:
template <typename T, std::size_t N, typename... Ts>
struct index_of;
//проходим кортеж и определяем типы данных
template <typename T, std::size_t N, typename... Ts>
struct index_of<T, N, T, Ts...> : std::integral_constant<int, N> {}
template <typename T, std::size_t N, typename U, typename... Ts>
struct index_of<T, N, U, Ts...> : std::integral_constant<int, index_of<T, N + 1, Ts...>::value> {};template <typename T, std::size_t N>
struct index_of<T, N> : std::integral_constant<int, -1> {};
//находим первые совпадения и возвращаем
template <typename T, typename... Ts>
constexpr T get_first(const std::tuple<Ts...>& tup) {
return std::move(std::get<index_of<T, 0, Ts...>::value>(tup));
}
int main(){
constexpr auto tup = std::make_tuple('x', 10, 'y', 1.0f);
std::cout << get_first<int>(tup);
std::cout << get_first<char>(tup);
std::cout << get_first<float>(tup);
}
Комментарии