You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++11/C++14中实现std::regular_invocable类型特性?

在C++14中实现std::regular_invocable元函数

背景

我正在开发一个Arduino“GameBoy”风格项目,要制作类游戏引擎的库。使用AVR板时Arduino IDE没有C++ STL,所以需要自行实现STL中的容器和类型特性组件。目前需要把std::regular_invocable实现为元函数——因为Arduino IDE基于C++14,不支持concepts。但std::regular_invocable依赖requires表达式隐式保证等式保持,实现起来遇到了困难。我已经完成了std::is_invocable和std::invoke的实现。

已实现的std::invocable代码

namespace __detail {
  
  struct __do_invocable_concept_impl {
    template<class _Fn, class... _Args,
      class = decltype(invoke(forward<_Fn>(declval<_Fn&&>()), forward<_Args>(declval<_Args&&>())...))>
    static true_type __test(int);

    template<class, class>
    static false_type __test(...);
  };

  template<class _Fn, class... _Args>
  struct __invocable_concept_impl : public __do_invocable_concept_impl {
    using type = decltype(__test<_Fn, _Args...>(0));
  };

}

template<class _Fn, class... _Args>
struct invocable 
  : public __detail::__invocable_concept_impl<_Fn, _Args...>::type { };

libstdc++中的std::invocable和std::regular_invocable实现

template<class _Fn, class... _Args>
concept invocable = 
  requires(_Fn&& __f, _Args&&... __args) {
    invoke(forward<_Fn>(__f), forward<_Args>(__args)...);
  };

template<class _Fn, class... _Args>
concept regular_invocable = invocable<_Fn, _Args...>; 
// 此实现自动隐含等式保持的语义要求

问题

我对新版C的requires表达式不太熟悉,仅了解它能创建未初始化变量用于函数调用和比较。想问:不依赖requires表达式的情况下,如何在C14中检查可调用类型及其实参在调用后是否保持等式?

我曾尝试用declval结合bool_constant检查等式,但无法比较传入invoke的_Fn&&类型——因为declval处于未求值上下文。我需要在编译时检查_Fn类型变量和_Args...中的变量在调用后是否仍相等。


解决方案

首先要明确:C14没有requires表达式的话,无法完全模拟concepts对regular_invocable的等式保持检查——因为regular_invocable的等式保持是语义要求,而非编译期可直接验证的语法约束。libstdc里的regular_invocable看起来只是继承invocable,但实际上它的等式保持是隐含的语义规则,requires表达式并没有做语法层面的强制检查。

不过可以在编译期做语法层面的近似模拟,尽可能贴近语义要求:

1. 实现思路

  • 先复用已有的invocable确保可调用性合法;
  • 对左值参数检查其是否支持相等比较(因为右值参数调用后不需要保持等式,通常会被移动消耗);
  • 模拟调用前后左值参数的可比较性是否依然成立。

2. 代码实现

#include <type_traits>
using namespace std;

// 辅助trait:检查类型是否支持相等比较
namespace __detail {
template<class T, class = void>
struct is_equal_comparable : false_type {};

template<class T>
struct is_equal_comparable<T, decltype(void(declval<T&>() == declval<T&>()))> : true_type {};

// 复用已有的invocable trait
template<class _Fn, class... _Args>
using is_invocable_t = typename invocable<_Fn, _Args...>::type;

// 测试左值参数调用后的可比较性保持
template<class _Fn, class... _Args,
         typename = enable_if_t<is_invocable_t<_Fn, _Args&...>::value>>
static true_type __test_regular_lvalue(int) {
    // 验证调用后所有左值参数仍可相等比较
    using check_eq = bool_constant<(is_equal_comparable<_Args>::value && ...)>;
    return check_eq{};
}

template<class, class...>
static false_type __test_regular_lvalue(...);

// 核心实现逻辑
template<class _Fn, class... _Args>
struct __regular_invocable_impl {
    static constexpr bool base_invocable = is_invocable_t<_Fn, _Args...>::value;
    // 左值参数检查 + 基础可调用性检查
    static constexpr bool value = base_invocable && decltype(__test_regular_lvalue<_Fn, _Args...>(0))::value;
};
}

// 对外暴露的regular_invocable trait
template<class _Fn, class... _Args>
struct regular_invocable : bool_constant<__detail::__regular_invocable_impl<_Fn, _Args...>::value> {};

template<class _Fn, class... _Args>
constexpr bool regular_invocable_v = regular_invocable<_Fn, _Args...>::value;

3. 局限性说明

  • 仅能检查语法层面的可比较性,无法验证语义上的等式保持(比如调用函数后参数的值是否真的未改变)——这在C++14中无法通过编译期元编程实现;
  • 对右值参数不做检查,符合STLregular_invocable的语义(右值本身不要求调用后保持状态);
  • 如果需要检查可调用对象本身(_Fn)的左值调用后等式保持,可以扩展代码,将_Fn&加入参数列表进行同等检查。

内容的提问来源于stack exchange,提问作者Andrey Stroitelev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 15:47:51