Реализация идиомы обнаружения для C++20, основанная на предложении N4502.
#include <scl/utility/type_traits/detection.h>Идиома обнаружения предоставляет чистый и стандартизированный способ проверки корректности выражений на этапе компиляции с использованием техники SFINAE (Substitution Failure Is Not An Error — ошибка подстановки не является ошибкой). Это особенно полезно для:
is_detected<Operation, Arguments... >Проверяет, является ли Operation<Arguments...> корректным выражением.
Возвращает: std::true_type или std::false_type
template <typename T>
using has_size_t = decltype(std:: declval<T>().size());
static_assert(scl::is_detected<has_size_t, std::vector<int>>::value);
is_detected_v<Operation, Arguments...> (C++17)Вариабельный шаблон для is_detected.
Возвращает: bool
if constexpr (scl::is_detected_v<has_size_t, MyType>) {
// Тип имеет член size()
}
detected_t<Operation, Arguments... >Извлекает тип Operation<Arguments...> или возвращает void, если выражение некорректно.
template <typename T>
using value_type_t = typename T::value_type;
using VecValueType = scl::detected_t<value_type_t, std::vector<int>>;
// VecValueType — это int
detected_or_t<Default, Operation, Arguments...>Извлекает тип Operation<Arguments...> или возвращает Default, если выражение некорректно.
using ValueType = scl::detected_or_t<void, value_type_t, MyType>;
// ValueType — это MyType:: value_type, если он существует, иначе void
is_detected_exact<Expected, Operation, Arguments...>Проверяет, что Operation<Arguments...> возвращает в точности тип Expected.
static_assert(
scl::is_detected_exact_v<int, value_type_t, std::vector<int>>
);
is_detected_convertible<To, Operation, Arguments...>Проверяет, что Operation<Arguments...> приводится к типу To.
static_assert(
scl::is_detected_convertible_v<double, value_type_t, std::vector<int>>
);
template <typename T>
using has_serialize_t = decltype(std::declval<T>().serialize());
template <typename T>
void process(const T& obj) {
if constexpr (scl::is_detected_v<has_serialize_t, T>) {
obj.serialize();
} else {
// Запасное поведение
}
}
template <typename T>
using value_type_t = typename T:: value_type;
template <typename Container>
auto sum(const Container& c) {
using ValueType = scl::detected_or_t<int, value_type_t, Container>;
ValueType result{};
for (const auto& elem : c) {
result += elem;
}
return result;
}
template <typename T>
using has_begin_end_t = decltype(
std::declval<T>().begin(),
std::declval<T>().end()
);
template <typename T>
inline constexpr bool is_iterable_v = scl::is_detected_v<has_begin_end_t, T>;
template <typename T>
requires is_iterable_v<T>
void print(const T& container) {
for (const auto& elem : container) {
std::cout << elem << " ";
}
}
template <typename T, typename U>
using has_plus_t = decltype(std::declval<T>() + std::declval<U>());
template <typename T, typename U>
inline constexpr bool is_addable_v = scl::is_detected_v<has_plus_t, T, U>;
template <typename T, typename U>
auto safe_add(const T& a, const U& b) {
if constexpr (is_addable_v<T, U>) {
return a + b;
} else {
return T{};
}
}
template <typename T>
using is_container = std::conjunction<
scl::is_detected<has_begin_end_t, T>,
scl::is_detected<value_type_t, T>,
scl::is_detected<has_size_t, T>
>;
template <typename T>
inline constexpr bool is_container_v = is_container<T>::value;
template <typename T, std::enable_if_t<is_container_v<T>, int> = 0>
void process_container(const T& container) {
// Реализация
}
Идиома обнаружения — это средство исключительно времени компиляции, для которого характерно:
Хотя концепты C++20 предоставляют более мощный и выразительный способ наложения ограничений на шаблоны, идиома обнаружения остаётся полезной для:
detected_t// Версия с концептами (C++20)
template <typename T>
concept HasSize = requires(T t) {
{ t.size() } -> std::convertible_to<std::size_t>;
};
// Версия с идиомой обнаружения
template <typename T>
using has_size_t = decltype(std::declval<T>().size());
template <typename T>
inline constexpr bool has_size_v =
scl::is_detected_convertible_v<std::size_t, has_size_t, T>;