Hatred's Log Place

DON'T PANIC!

Jun 1, 2026 - 2 minute read - Programming

C++23: проверка на присутствие поля структуры

Небольшой мемориз. Пока не дошло до мышечной памяти.

Естественно - через концепты.

Свой концепт

Самый простой вариант: объявить концепт на проверку нужных полей:

#include <concepts>

template<typename T>
concept has_member_variables = requires (T t) {
    { auto(t.f) } -> std::floating_point;
    { auto(t.i) } -> std::integral;
};  

struct foo { float f; int i; };
struct bar { float f; };
struct baz { float i; int z; };

static_assert(has_member_variables<foo>);
static_assert(!has_member_variables<bar>);
static_assert(!has_member_variables<baz>);

Может статься полезным на всякие ограничения на классы. Погонять: https://godbolt.org/z/WsYx7P4b9

Ветвления в коде

Понятно, что не runtime, а compiletime. Может оказаться полезным для какого-то опционального поведения: к примеру: если класс представляет какое-то поле - то использовать его значение, в противном случае - какой-то дефолт.

Выглядеть это может как-то так:

if constexpr (requires (T t) { { auto(t.g) } -> std::floating_point; }) {
    return val.g;
}

Полный пример ( https://godbolt.org/z/zsjbrnxoT):

#include <concepts>
#include <print>

struct Earth {};
struct Moon {float g{1.625f}; };

template<typename T>
float get_g(const T& val)
{
    if constexpr (requires (T t) { { auto(t.g) } -> std::floating_point; }) {
        return val.g;
    }
    return 9.82f;
}

int main()
{
    std::println("Earth g={}", get_g(Earth{}));
    std::println("Moon g={}", get_g(Moon{}));
}

“Колбеки”

Это уже не про поля в коде. Речь про выполнение каких-то функций или методов, если они предоставлены пользователем. К примеру, при использовании CRTM: можно предоставить “колбеки”, on_start(), on_stop(), on_error() и так далее и если они реализованы, то вызывать этот код, в противном случае - нет.

Вызов будет очень похожим на проверку поля, только не будет auto(EXPR):

if constexpr (requires (T t) { { t.get_g() } -> std::floating_point; }) {
    return val.get_g();
}

И полный пример ( https://godbolt.org/z/8xrWKoTsr):

#include <concepts>
#include <print>

struct Earth {};
struct Moon {
    constexpr float get_g() const noexcept {
        return 1.625f;
    }
};

template<typename T>
float constexpr get_g(const T& val)
{
    if constexpr (requires (T t) { { t.get_g() } -> std::floating_point; }) {
        return val.get_g();
    }
    return 9.82f;
}

int main()
{
    std::println("Earth g={}", get_g(Earth{}));
    std::println("Moon g={}", get_g(Moon{}));
}

Если же тип возвращаемого значения вообще не имеет значения, то проверка упростится до:

if constexpr (requires (T t) { t.do_process(); }) {
    val.do_process();
}