找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2209

积分

0

好友

313

主题
发表于 昨天 01:01 | 查看: 2| 回复: 0

在探讨了C++模板元编程中的逻辑操作后,一个自然的问题是:如果在 C++17 之前没有提供这些逻辑模板,我们应该如何实现它们呢?虽然已经有了现成的工具,但尝试自己动手“逆向”并实现一个类似的工具,是否能够帮助我们更深入地理解其背后的原理和机制呢?本文将尝试手动实现 std::conjunction,一探其究竟。

一、标准定义与库实现分析

我们先来回顾一下 std::conjunction 在 cppreference 上的典型实现:

template<class...>
struct conjunction : std::true_type {};

template<class B1>
struct conjunction<B1> : B1 {};

template<class B1, class... Bn>
struct conjunction<B1, Bn...>
    : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};

再看看某个标准库中的具体实现(以 __and_ 内部类为例):

template<typename...>
struct __and_;

template<>
struct __and_<>
    : public true_type
    { };

template<typename _B1>
struct __and_<_B1>
    : public _B1
    { };

template<typename _B1, typename _B2>
struct __and_<_B1, _B2>
    : public __conditional_t<_B1::value, _B2, _B1>
    { };

template<typename _B1, typename _B2, typename _B3, typename... _Bn>
struct __and_<_B1, _B2, _B3, _Bn...>
    : public __conditional_t<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>
    { };

template<typename... _Bn>
struct conjunction
    : __and_<_Bn...>
    { };

两者的实现思路基本一致:首先通过特化版本来处理变参模板的终止条件(如果不理解,可以参考变参模板相关的文章),然后利用 conditional_t 递归地处理逻辑类型的 value 结果。

二、实现源码与详细解析

除了使用 std::conditional_t,我们是否可以用其他方法来实现类似的逻辑操作呢?让我们看看下面的代码实现:

#include <iostream>
#include <type_traits>

// 普通模板,空参数返回 true_type
template <typename...> struct __and__ : std::true_type {};

// 递归特化并检查条件
template <typename T, typename... Rest> struct __and__<T, Rest...> : std::integral_constant<bool, T::value && __and__<Rest...>::value> {};

// 模拟 AND 实现的别名模板
template <typename... Cond> using __and_t__ = __and__<Cond...>;

// 多条件检测模板
template <typename T, typename = void> struct check_mul_attr : std::false_type {};

template <typename T>
struct check_mul_attr<T, std::void_t<decltype(std::declval<T>().test()), decltype(std::declval<T>().display()), typename T::nestType>>
    : __and_t__<std::is_same<decltype(std::declval<T>().test()), void>,
                std::is_same<decltype(std::declval<T>().display()), int>,
                std::is_convertible<typename T::nestType, int>> {};

// 测试结构体
struct AllAttr {
    void test(){}
    int display(){ return 0; }
    using nestType = int;
};

struct PartialAttr {
    void test(){}
    using nestType = int;
};

struct Nothing {};

int main(){
    // 测试 AND 逻辑在类型检查中的应用
    std::cout << "all have: " << check_mul_attr<AllAttr>::value << std::endl;    // 1
    std::cout << "partial: " << check_mul_attr<PartialAttr>::value << std::endl; // 0
    std::cout << "nothing: " << check_mul_attr<Nothing>::value << std::endl;     // 0

    // 测试 __and_t__ 基础功能
    std::cout << "test and result True: " << __and_t__<std::true_type, std::true_type>::value << std::endl;   // 1
    std::cout << "test and result False: " << __and_t__<std::true_type, std::false_type>::value << std::endl; // 0

    return 0;
}

为了更清晰地理解编译器的展开过程,我们可以查看上述代码编译展开后的结果(由编译器或工具生成):

// ... (此处为冗长的展开代码,展示了模板实例化的具体过程)
// 关键部分:展示了 __and__ 模板如何递归展开并计算最终的布尔值
template<typename T, typename ... Rest>
struct __and__<T, Rest...> : public std::integral_constant<bool, T::value && __and__<Rest...>::value>
{
};
// ...

在这段代码的实现中,有几个关键点需要说明:

  1. std::void_t 的作用std::void_t 是一个工具,如果其所有模板参数都是良构的类型,那么它就会产生 void 类型。如果其中任何一个参数(如 decltype(...)typename T::nestType)无效(即类型不存在),那么 SFINAE 规则会使该特化版本被丢弃,从而回退到默认的 std::false_type 版本。这样,我们就实现了对类型是否包含特定成员函数或嵌套类型的检查。
  2. decltype(std::declval<T>().test()) 的用法:这里之所以需要 std::declval,是因为我们需要在编译期“假装”有一个 T 类型的对象,以便查询其成员函数 test 的返回类型。如果 Ttest 函数是静态的,我们可以直接使用 decltype(T::test())。但对于非静态成员函数,就必须通过这种“创造”一个右值引用来访问。
  3. check_mul_attr 的逻辑:该模板的默认版本继承自 std::false_type。另一个偏特化版本则尝试使用 std::void_t 来检查三个条件。只有当这三个条件都满足(即 void_t 成功生成 void 类型)时,才会选择这个特化版本。这个特化版本内部又使用了我们手写的 __and_t__,来对三个具体的类型特征(返回类型、可转换性)进行逻辑与操作。

三、总结

重新“造轮子”是好是坏,并没有一个标准答案。但如果目的是为了学习,并且这个“轮子”的规模适中,那么这无疑是一件好事。学习最忌讳的就是“知其然,而不知其所以然”。如果仅仅停留在会用的层面,往往很难灵活、主动地将各种技术有机结合起来解决复杂问题。很多时候,这会导致一种高级的“生搬硬套”,从而埋下各种隐患。这些隐患未必会立即暴露,但一旦爆发,可能就是难以解决的问题。

通过手动实现 std::conjunction 并深入分析其与 std::void_t、SFINAE 等技术的结合,我们对 C++ 模板元编程 的逻辑操作有了更本质的理解。这种底层探索,正是从“会用”走向“精通”的关键一步。如果想了解更多关于 C++ 底层机制或 STL 源码的深度解析,欢迎到 云栈社区 与更多开发者一起交流探讨。




上一篇:Kubernetes 核心解析:为何说它是一个“不相信人”的自动化系统?
下一篇:锐捷RG-Rain310W V2云终端流出:i3-7100U准系统,支持M.2与2.5寸SATA
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-16 02:06 , Processed in 0.436020 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表