scl-utility

Detection Idiom

A C++20 implementation of the Detection Idiom based on N4502.

Overview

The Detection Idiom provides a clean, standardized way to detect the validity of expressions at compile-time using SFINAE (Substitution Failure Is Not An Error). This is particularly useful for:

Features

API Reference

Basic Detection

is_detected<Operation, Arguments... >

Checks if Operation<Arguments...> is a valid expression.

Returns: std::true_type or 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)

Variable template version of is_detected.

Returns: bool

if constexpr (scl::is_detected_v<has_size_t, MyType>) {
    // Type has size() member
}

Type Extraction

detected_t<Operation, Arguments...>

Extracts the type of Operation<Arguments...>, or void if ill-formed.

template <typename T>
using value_type_t = typename T:: value_type;

using VecValueType = scl::detected_t<value_type_t, std::vector<int>>;
// VecValueType is int

detected_or_t<Default, Operation, Arguments...>

Extracts the type of Operation<Arguments...>, or Default if ill-formed.

using ValueType = scl::detected_or_t<void, value_type_t, MyType>;
// ValueType is MyType:: value_type if it exists, otherwise void

Advanced Detection

is_detected_exact<Expected, Operation, Arguments...>

Checks if Operation<Arguments...> results in exactly the Expected type.

static_assert(
    scl::is_detected_exact_v<int, value_type_t, std::vector<int>>
);

is_detected_convertible<To, Operation, Arguments... >

Checks if Operation<Arguments...> is convertible to To.

static_assert(
    scl:: is_detected_convertible_v<double, value_type_t, std::vector<int>>
);

Usage Examples

Example 1: Detecting Member Functions

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 {
        // Fallback behavior
    }
}

Example 2: Detecting Member Types

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;
}

Example 3: Type Traits

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 << " ";
    }
}

Example 4: Detecting Operators

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{};
    }
}

Example 5: Concept-like Constraints (pre-C++20)

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) {
    // Implementation
}

Performance

The Detection Idiom is a compile-time only facility with:

Comparison with C++20 Concepts

While C++20 Concepts provide a more powerful and expressive way to constrain templates, the Detection Idiom remains useful for:

// Concept version (C++20)
template <typename T>
concept HasSize = requires(T t) {
    { t.size() } -> std::convertible_to<std::size_t>;
};

// Detection Idiom version
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>;

References